package com.cloudweb.oa.controller;

import cn.js.fan.db.ListResult;
import cn.js.fan.db.SQLFilter;
import cn.js.fan.security.ThreeDesUtil;
import cn.js.fan.util.*;
import cn.js.fan.util.file.FileUtil;
import cn.js.fan.web.Global;
import cn.js.fan.web.SkinUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.cloudweb.oa.api.*;
import com.cloudweb.oa.bean.ExportExcelItem;
import com.cloudweb.oa.cache.*;
import com.cloudweb.oa.entity.User;
import com.cloudweb.oa.exception.ValidateException;
import com.cloudweb.oa.security.AuthUtil;
import com.cloudweb.oa.service.*;
import com.cloudweb.oa.service.impl.WorkflowHelper;
import com.cloudweb.oa.utils.*;
import com.cloudweb.oa.vo.CommonConstant;
import com.cloudweb.oa.vo.Result;
import com.cloudwebsoft.framework.aop.ProxyFactory;
import com.cloudwebsoft.framework.db.JdbcTemplate;
import com.cloudwebsoft.framework.util.LogUtil;
import com.redmoon.kit.util.FileInfo;
import com.redmoon.kit.util.FileUpload;
import com.redmoon.oa.Config;
import com.redmoon.oa.android.Privilege;
import com.redmoon.oa.base.IAttachment;
import com.redmoon.oa.base.IFormDAO;
import com.redmoon.oa.base.IFormMacroCtl;
import com.redmoon.oa.db.SequenceManager;
import com.redmoon.oa.flow.Attachment;
import com.redmoon.oa.flow.AttachmentLogDb;
import com.redmoon.oa.flow.FormDAO;
import com.redmoon.oa.flow.FormDAOMgr;
import com.redmoon.oa.flow.Render;
import com.redmoon.oa.flow.*;
import com.redmoon.oa.flow.macroctl.MacroCtlMgr;
import com.redmoon.oa.flow.macroctl.MacroCtlUnit;
import com.redmoon.oa.kernel.License;
import com.redmoon.oa.message.IMessage;
import com.redmoon.oa.message.MessageDb;
import com.redmoon.oa.person.PlanDb;
import com.redmoon.oa.person.UserDb;
import com.redmoon.oa.person.UserMgr;
import com.redmoon.oa.person.UserSetupDb;
import com.redmoon.oa.pvg.RoleDb;
import com.redmoon.oa.security.SecurityUtil;
import com.redmoon.oa.shell.BSHShell;
import com.redmoon.oa.sms.SMSFactory;
import com.redmoon.oa.stamp.StampDb;
import com.redmoon.oa.stamp.StampPriv;
import com.redmoon.oa.sys.DebugUtil;
import com.redmoon.oa.ui.LocalUtil;
import com.redmoon.oa.ui.SkinMgr;
import com.redmoon.oa.util.WordUtil;
import com.redmoon.oa.visual.*;
import com.redmoon.weixin.mgr.FlowDoMgr;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.tools.zip.ZipOutputStream;
import org.json.JSONException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.zip.CRC32;
import java.util.zip.CheckedOutputStream;


@Api(tags = "流程")
@Slf4j
@Controller
public class WorkflowController {
    @Autowired
    private HttpServletRequest request;

    @Autowired
    private WorkflowService workflowService;

    @Autowired
    private ThreadContext threadContext;

    @Autowired
    private ResponseUtil responseUtil;

    @Autowired
    private I18nUtil i18nUtil;

    @Autowired
    private IFileService fileService;

    @Autowired
    private AuthUtil authUtil;

    @Autowired
    JwtUtil jwtUtil;

    @Autowired
    UserCache userCache;

    @Autowired
    SysUtil sysUtil;

    @Autowired
    DepartmentCache departmentCache;

    @Autowired
    ConfigUtil configUtil;

    @Autowired
    IWorkflowProService workflowProService;

    @Autowired
    IWorkflowPlusService workflowPlusService;

    @Autowired
    FlowFormDaoCache flowFormDaoCache;

    @Autowired
    IFlowRender flowRender;

    @Autowired
    ExportExcelCache exportExcelCache;

    @Autowired
    IObsServiceFactory obsServiceFactory;

    @Autowired
    RoleCache roleCache;

    @Autowired
    IRoleService roleService;

    @Autowired
    ModuleService moduleService;

    @Autowired
    ZipUtil zipUtil;

    @Autowired
    IDeptUserService deptUserService;

    @Autowired
    IPoiTlUtil poiTlUtil;

    @Autowired
    ILicenseUtil licenseUtil;

    @Autowired
    SysProperties sysProperties;

    @Autowired
    IUserService userService;
    @Autowired
    private WorkflowHelper workflowHelper;

    @ApiOperation(value = "保存列表设置", notes = "保存列表设置", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "typeCode", value = "流程类型", required = true, dataType = "String"),
            @ApiImplicitParam(name = "fields", value = "列表中的字段，以逗号分隔", required = false, dataType = "String"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/saveColProps", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> saveColProps(@RequestParam(value = "typeCode", defaultValue = "") String typeCode, @RequestParam(value = "fields", required = false) String fields) {
        Leaf lf = new Leaf();
        if ("".equals(typeCode)) {
            typeCode = Leaf.CODE_ROOT;
        }
        lf = lf.getLeaf(typeCode);
        String colProps = lf.getColProps();
        JSONArray aryColProps = null;
        if (!StrUtil.isEmpty(colProps)) {
            aryColProps = com.alibaba.fastjson.JSONArray.parseArray(colProps);
            if (aryColProps == null) {
                LogUtil.getLog(getClass()).error("json格式非法：" + colProps);
                // aryColProps = new JSONArray();
            }
        }

        FormDb fd = new FormDb();
        fd = fd.getFormDb(lf.getFormCode());

        JSONArray arr = new JSONArray();
        String[] arrCol = fields.split(",");
        // 顺序拼接colProps
        for (String field : arrCol) {
            // 当清空时，fields中为空字符串，field的值也为空字符串
            if ("".equals(field)) {
                continue;
            }
            boolean isFound = false;
            if (aryColProps!=null && aryColProps.size() > 0) {
                for (Object o : aryColProps) {
                    JSONObject jsonObject = (JSONObject)o;
                    if (field.equals(jsonObject.getString("field")) || field.equals(jsonObject.getString("name"))) {
                        isFound = true;
                        // 清洗
                        if (field.equals(jsonObject.getString("name"))) {
                            jsonObject.put("field", field);
                            jsonObject.remove("name");
                        }
                        jsonObject.put("sort", Leaf.canSort(field));
                        arr.add(jsonObject);
                        break;
                    }
                }
            }
            if (!isFound) {
                JSONObject json = new JSONObject();
                FormField ff = null;
                // 有可能选择了根节点
                if (fd.isLoaded()) {
                    ff = fd.getFormField(field);
                }
                String title;
                if (ff == null) {
                    title = Leaf.getFlowColTitle(field);
                } else {
                    title = ff.getTitle();
                }
                json.put("title", title);
                json.put("field", field);
                json.put("width", 150);
                json.put("sort", Leaf.canSort(field));
                json.put("align", "center");
                json.put("hide", false);
                arr.add(json);
            }
        }

        lf.setColProps(arr.toString());
        return new Result<>(lf.update());
    }

    @ApiOperation(value = "保存列表设置", notes = "保存列表设置", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "typeCode", value = "流程类型", required = true, dataType = "String"),
            @ApiImplicitParam(name = "field", value = "字段", required = false, dataType = "String"),
            @ApiImplicitParam(name = "width", value = "宽度", required = false, dataType = "Double"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/saveColWidth", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> saveColWidth(@RequestParam(value = "typeCode", required = false) String typeCode, @RequestParam(value = "field", required = false) String field, @RequestParam(value = "width", required = false)Double width) {
        Leaf lf = new Leaf();
        if ("".equals(typeCode)) {
            typeCode = Leaf.CODE_ROOT;
        }
        lf = lf.getLeaf(typeCode);
        String colProps = lf.getColProps();
        int displayMode = ParamUtil.getInt(request, "displayMode", WorkflowMgr.DISPLAY_MODE_MINE);
        JSONArray aryColProps;
        if (!StrUtil.isEmpty(colProps)) {
            aryColProps = com.alibaba.fastjson.JSONArray.parseArray(colProps);
            if (aryColProps == null) {
                LogUtil.getLog(getClass()).error("json格式非法：" + colProps);
                return new Result<>(false, "json格式非法：" + colProps);
            }
            if (aryColProps.size() == 0) {
                aryColProps = com.redmoon.oa.flow.Leaf.getDefaultColProps(request, typeCode, displayMode);
            }
        }
        else {
            aryColProps = com.redmoon.oa.flow.Leaf.getDefaultColProps(request, typeCode, displayMode);
        }
        for (Object o : aryColProps) {
            JSONObject json = (JSONObject)o;
            // 清洗老的格式，将name改为field
            if (json.containsKey("name")) {
                json.put("field", json.remove("name"));
            }
            if (field.equals(json.getString("field"))) {
                json.put("width", width.intValue());
                break;
            }
        }

        lf.setColProps(aryColProps.toString());
        return new Result<>(lf.update());
    }

    @ApiOperation(value = "置待办记录的状态，用于流程干预", notes = "置待办记录的状态，用于流程干预", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "myActionId", value = "待办记录ID", required = true, dataType = "Long"),
            @ApiImplicitParam(name = "checkStatus", value = "状态", required = false, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/setMyActionStatus", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> setMyActionStatus(@RequestParam(value = "myActionId", required = false) Long myActionId, @RequestParam(value = "checkStatus", required = false) Integer checkStatus) {
        JSONObject json = new JSONObject();
        WorkflowMgr wm = new WorkflowMgr();
        boolean re = false;
        try {
            int actionStatus = wm.setMyActionStatus(request, myActionId, checkStatus);
            json.put("actionStatus", actionStatus);
            json.put("ret", "1");
            json.put("msg", "操作成功！");
        } catch (ErrMsgException e) {
            return new Result<>(false, e.getMessage());
        }
        return new Result<>(json);
    }

    @ApiOperation(value = "清除节点上的用户", notes="清除节点上的用户", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "actionId", value = "流程节点ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/clearActionUser", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> clearActionUser(@RequestParam(value = "actionId") Integer actionId) {
        boolean re;
        try {
            WorkflowActionDb wa = new WorkflowActionDb();
            wa = wa.getWorkflowActionDb(actionId);
            wa.setUserName("");
            wa.setUserRealName("");
            re = wa.save();
        } catch (ErrMsgException e) {
            LogUtil.getLog(getClass()).error(e);
            return new Result<>(false, e.getMessage());
        }
        return new Result<>(re);
    }

    @ApiOperation(value = "流程批量提交", notes="流程批量提交", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "ids", value = "流程ID，以逗号分隔", required = true, dataType = "String"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/finishBatch", method = RequestMethod.POST, produces = {"application/json;"})
    public Result<Object> finishBatch(@RequestParam(value="ids")String ids) {
        try {
            workflowPlusService.FinishActionBatch(request, ids);
        } catch (ErrMsgException e) {
            return new Result<>(false, e.getMessage());
        }
        return new Result<>(true);
    }

    @ApiOperation(value = "列出下载日志", notes="列出下载日志", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "attId", value = "附件ID", required = true, dataType = "Long"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/listAttLog", method = RequestMethod.POST, produces = {"text/html;", "application/json;charset=UTF-8;"})
    public Result<Object> listAttLog(@RequestParam(value = "flowId", required = false, defaultValue = "-1") Integer flowId, @RequestParam(value = "attId", required = false, defaultValue = "-1") Long attId) {
        AttachmentLogDb ald = new AttachmentLogDb();
        String sql = ald.getQuery(request, flowId, attId);
        LogUtil.getLog(getClass()).info(sql);
        int pageSize = ParamUtil.getInt(request, "pageSize", 20);
        int curPage = ParamUtil.getInt(request, "page", 1);
        ListResult lr = null;
        try {
            lr = ald.listResult(sql, curPage, pageSize);
        } catch (ResKeyException e) {
            LogUtil.getLog(getClass()).error(e);
        }

        JSONArray rows = new JSONArray();
        JSONObject jobject = new JSONObject();

        jobject.put("list", rows);
        jobject.put("total", lr.getTotal());

        WorkflowDb wf = new WorkflowDb();
        UserDb user = new UserDb();
        for (Object o : lr.getResult()) {
            ald = (AttachmentLogDb) o;
            JSONObject jo = new JSONObject();
            jo.put("id", String.valueOf(ald.getLong("id")));
            jo.put("logTime", DateUtil.format(ald.getDate("log_time"), "yyyy-MM-dd HH:mm:ss"));
            jo.put("flowId", ald.getInt("flow_id"));
            jo.put("flowTitle", wf.getWorkflowDb(ald.getInt("flow_id")).getTitle());

            user = user.getUserDb(ald.getString("user_name"));
            jo.put("realName", user.getRealName());

            Attachment att = new Attachment((int) ald.getLong("att_id"));
            jo.put("attName", att.getName());

            jo.put("logType", AttachmentLogDb.getTypeDesc(ald.getInt("log_type")));
            rows.add(jo);
        }

        return new Result<>(jobject);
    }

    @ApiOperation(value = "删除附件下载日志", notes="删除附件下载日志", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "ids", value = "ID以逗号分隔", required = true, dataType = "String"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/delAttLog", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> delAttLog(@RequestParam(value = "ids", required = false) String ids) {
        String[] ary = StrUtil.split(ids, ",");
        if (ary == null) {
            return new Result<>(false, "请选择记录");
        }
        boolean re = false;
        try {
            for (String strId : ary) {
                long id = StrUtil.toLong(strId, -1);
                if (id != -1) {
                    AttachmentLogDb ald = new AttachmentLogDb();
                    ald = (AttachmentLogDb) ald.getQObjectDb(id);

                    boolean isValid = false;
                    com.redmoon.oa.pvg.Privilege pvg = new com.redmoon.oa.pvg.Privilege();
                    if (pvg.isUserPrivValid(request, "admin")) {
                        isValid = true;
                    } else {
                        long flowId = ald.getLong("flow_id");
                        WorkflowDb wf = new WorkflowDb();
                        wf = wf.getWorkflowDb((int) flowId);
                        if (wf != null) {
                            LeafPriv lp = new LeafPriv(wf.getTypeCode());
                            if (pvg.isUserPrivValid(request, "admin.flow")) {
                                if (lp.canUserExamine(pvg.getUser(request))) {
                                    isValid = true;
                                }
                            }
                        } else {
                            // 对应的流程如不存在，则允许删除
                            isValid = true;
                        }
                    }

                    if (!isValid) {
                        return new Result<>(false, "权限非法");
                    }

                    if (isValid) {
                        re = ald.del();
                    }
                } else {
                    return new Result<>(false, "标识非法");
                }
            }
        } catch (ResKeyException e) {
            LogUtil.getLog(getClass()).error(e);
        }
        return new Result<>(re);
    }

    @ApiOperation(value = "流程列表", notes = "流程列表", httpMethod = "POST")
    @ApiImplicitParam(name = "type", value = "列表类型", required = false, dataType = "String")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/list", method = RequestMethod.POST, produces = {"text/html;", "application/json;charset=UTF-8;"})
    public Result<Object> list(@RequestParam(value = "type", required = false) String type) {
        // 默认值用DISPLAY_MODE_DOING，而原先是DISPLAY_MODE_SEARCH，防止360浏览器传上来的参数异常，致直接进入查询，而导致看到所有人的待办流程
        int displayMode;
        // 显示模式，0表示流程查询、1表示待办、2表示我参与的流程、3表示我发起的流程
        if ("mine".equals(type)) {
            displayMode = WorkflowMgr.DISPLAY_MODE_MINE;
        }
        else if ("attend".equals(type)) {
            displayMode = WorkflowMgr.DISPLAY_MODE_ATTEND;
        }
        else if ("favorite".equals(type)) {
            displayMode = WorkflowMgr.DISPLAY_MODE_FAVORIATE;
        }
        else if ("search".equals(type)) {
            displayMode = WorkflowMgr.DISPLAY_MODE_SEARCH;
        }
        else if ("urge".equals(type)) {
            displayMode = WorkflowMgr.DISPLAY_MODE_URGE;
        }
        else if ("expired".equals(type)) {
            displayMode = WorkflowMgr.DISPLAY_MODE_EXPIRED;
        }
        else if ("read".equals(type)) {
            displayMode = WorkflowMgr.DISPLAY_MODE_READ;
        }
        else if ("suspended".equals(type)) {
            displayMode = WorkflowMgr.DISPLAY_MODE_SUSPENDED;
        }
        else {
            // type=doing
            displayMode = WorkflowMgr.DISPLAY_MODE_DOING;
        }

        String typeCode = ParamUtil.get(request, "typeCode");
        String action = ParamUtil.get(request, "action"); // sel 选择我的流程

        MyActionDb mad = new MyActionDb();
        MacroCtlMgr mm = new MacroCtlMgr();
        FormDb fd = new FormDb();
        FormDAO fdao = new FormDAO();
        UserMgr um = new UserMgr();
        UserDb user;

        WorkflowDb wf = new WorkflowDb();
        com.alibaba.fastjson.JSONObject jobject = new com.alibaba.fastjson.JSONObject();
        Config cfg = Config.getInstance();
        jobject.put("flowOpStyle", StrUtil.toInt(cfg.get("flowOpStyle"), 1));
        int pageSize = ParamUtil.getInt(request, "pageSize", 20);
        int curPage = ParamUtil.getInt(request, "page", 1);
        String myUserName = authUtil.getUserName();
        com.redmoon.oa.pvg.Privilege pvg = new com.redmoon.oa.pvg.Privilege();
        Leaf leaf = new Leaf();
        if (displayMode == WorkflowMgr.DISPLAY_MODE_SEARCH) {
            if ("".equals(typeCode)) {
                if (pvg.isUserPrivValid(myUserName, "admin")) {
                    typeCode = Leaf.CODE_ROOT;
                } else {
                    LeafPriv lp = new LeafPriv(Leaf.CODE_ROOT);
                    if (lp.canUserQuery(myUserName)) {
                        typeCode = Leaf.CODE_ROOT;
                    } else {
                        jobject.put("list", new JSONArray());
                        return new Result<>(jobject);
                    }
                }
            }
        }

        if (!"".equals(typeCode)) {
            leaf = leaf.getLeaf(typeCode);
            if (leaf == null) {
                return new Result<>(false, "流程类型：" + typeCode + "不存在");
            }
            fd = fd.getFormDb(leaf.getFormCode());
        }

        com.redmoon.oa.sso.Config ssoconfig = new com.redmoon.oa.sso.Config();
        String desKey = ssoconfig.get("key");

        com.alibaba.fastjson.JSONArray colProps = null;
        if (leaf.isLoaded() && !"".equals(leaf.getColProps())) {
            colProps = com.alibaba.fastjson.JSONArray.parseArray(leaf.getColProps());
        }
        if (colProps == null || colProps.size() == 0) {
            colProps = com.redmoon.oa.flow.Leaf.getDefaultColProps(request, typeCode, displayMode);
        }
        String userRealName = "";

        // 显示模式，0表示流程查询、1表示待办、2表示我参与的流程、3表示我发起的流程、4表示我关注的流程、5表示督办的流程、6表示待阅的流程
        if (displayMode == WorkflowMgr.DISPLAY_MODE_DOING || displayMode == WorkflowMgr.DISPLAY_MODE_READ) {
            String sql;
            if (displayMode == WorkflowMgr.DISPLAY_MODE_READ) {
                sql = wf.getSqlByKind(request, WorkflowActionDb.KIND_READ);
            } else {
                boolean isDoReadSeparate = cfg.getBoolean("isDoReadSeparate");
                if (isDoReadSeparate) {
                    sql = wf.getSqlByKind(request, WorkflowActionDb.KIND_ACCESS);
                } else {
                    sql = wf.getSqlDoing(request);
                }
            }
            DebugUtil.i(getClass(), "list getSqlDoing=", sql);
            ListResult lr = null;
            try {
                lr = mad.listResult(sql, curPage, pageSize);
            } catch (ErrMsgException e) {
                LogUtil.getLog(getClass()).error(e);
            }
            com.alibaba.fastjson.JSONArray rows = new com.alibaba.fastjson.JSONArray();
            jobject.put("list", rows);
            jobject.put("page", curPage);
            jobject.put("total", lr.getTotal());

            WorkflowDb wfd = new WorkflowDb();
            for (Object o : lr.getResult()) {
                mad = (MyActionDb) o;
                wfd = wfd.getWorkflowDb((int) mad.getFlowId());
                String userName = wfd.getUserName();
                if (userName != null) {
                    user = um.getUserDb(wfd.getUserName());
                    userRealName = user.getRealName();
                }
                if (!typeCode.equals(wfd.getTypeCode())) { // 流程查询时，点击根节点，会显示所有的流程，此时typeCode可能与wfd.getTypeCode不一致
                    Leaf lf = leaf.getLeaf(wfd.getTypeCode());
                    if (lf == null) {
                        DebugUtil.e(getClass(), "list", "流程：" + wfd.getId() + "、" + wfd.getTitle() + " 类型：" + wfd.getTypeCode() + " 已不存在！");
                        continue;
                    } else {
                        fd = fd.getFormDb(lf.getFormCode());
                    }
                }
                fdao = fdao.getFormDAOByCache(wfd.getId(), fd);
                if (fdao == null) {
                    fdao = new FormDAO();
                    LogUtil.getLog(getClass()).warn("记录: flowId=" + wfd.getId() + " 已被删除");
                } else {
                    JSONObject jo = workflowService.getRow(wfd, fdao, colProps, um, userRealName, mad, mm, leaf, displayMode, action, desKey);
                    rows.add(jo);
                }
            }
        } else {
            String sql;
            if (displayMode == WorkflowMgr.DISPLAY_MODE_ATTEND) {
                sql = wf.getSqlAttend(request);
            } else if (displayMode == WorkflowMgr.DISPLAY_MODE_MINE) {
                sql = wf.getSqlMine(request);
            } else if (displayMode == WorkflowMgr.DISPLAY_MODE_FAVORIATE) {
                sql = wf.getSqlFavorite(request);
            }
            else if (displayMode == WorkflowMgr.DISPLAY_MODE_URGE) {
                sql = wf.getSqlUrge(request);
            }
            else if (displayMode == WorkflowMgr.DISPLAY_MODE_EXPIRED) {
                sql = wf.getSqlExpired(request);
            }
            else if (displayMode == WorkflowMgr.DISPLAY_MODE_SUSPENDED) {
                sql = wf.getSqlSuspended(request);
            }
            else {
                // 判断是否有权限，以防止非法操作
                LeafPriv lp = new LeafPriv(typeCode);
                if (!lp.canUserQuery(pvg.getUser(request))) {
                    // 如果用户没有权限，则返回我的流程所用的sql
                    sql = wf.getSqlAttend(request);
                } else {
                    sql = wf.getSqlSearch(request);
                }
            }

            DebugUtil.i(getClass(), "list sql=", sql);

            ListResult lr = null;
            try {
                lr = wf.listResult(sql, curPage, pageSize);
            } catch (ErrMsgException e) {
                LogUtil.getLog(getClass()).error(e);
            }

            // DebugUtil.i(getClass(), "list", "listResult 时长：" + (new Date().getTime() - t) + "毫秒 sql=" + sql);

            com.alibaba.fastjson.JSONArray rows = new com.alibaba.fastjson.JSONArray();
            jobject.put("list", rows);
            jobject.put("page", curPage);
            jobject.put("total", lr.getTotal());

            Iterator ir = lr.getResult().iterator();
            while (ir.hasNext()) {
                WorkflowDb wfd = (WorkflowDb) ir.next();
                if (!typeCode.equals(wfd.getTypeCode())) {
                    // 流程查询时，点击根节点，会显示所有的流程，此时typeCode可能与wfd.getTypeCode不一致
                    Leaf lf = leaf.getLeaf(wfd.getTypeCode());
                    if (lf == null) {
                        LogUtil.getLog(getClass()).error("流程：" + wfd.getId() + " " + wfd.getTitle() + " 其类型：" + wfd.getTypeCode() + " 已不存在！");
                        continue;
                    }
                    fd = fd.getFormDb(lf.getFormCode());
                } else {
                    fd = fd.getFormDb(leaf.getFormCode());
                }

                user = um.getUserDb(wfd.getUserName());
                if (user.isLoaded()) {
                    userRealName = user.getRealName();
                }

                fdao = fdao.getFormDAOByCache(wfd.getId(), fd);
                if (fdao == null) {
                    fdao = new FormDAO();
                    LogUtil.getLog(getClass()).error("记录: flowId=" + wfd.getId() + " 已被删除");
                } else {
                    com.alibaba.fastjson.JSONObject jo = workflowService.getRow(wfd, fdao, colProps, um, userRealName, mad, mm, leaf, displayMode, action, desKey);
                    rows.add(jo);
                }
            }
        }
        return new Result<>(jobject);
    }

    @ApiOperation(value = "流程抄送", notes="流程抄送", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/distribute", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> distribute(HttpServletRequest request, @RequestParam(defaultValue = "-1") Integer flowId) throws ResKeyException {
        String users = ParamUtil.get(request, "users");
        String roles = ParamUtil.get(request, "roles");
        String units = ParamUtil.get(request, "units");

        String paperTitle = ParamUtil.get(request, "title");

        com.redmoon.oa.Config cfg = com.redmoon.oa.Config.getInstance();
        boolean flowNotifyByEmail = cfg.getBooleanProperty("flowNotifyByEmail");
        int actionId = ParamUtil.getInt(request, "actionId", -1);

        try {
            workflowService.distribute(flowId, roles, users, units, paperTitle, true, flowNotifyByEmail, SMSFactory.isUseSMS(), actionId);
        } catch (ResKeyException e) {
            return new Result<>(false, e.getMessage(request));
        }
        return new Result<>(true);
    }

    @ApiOperation(value = "撤回，也用于流程干预", notes="撤回，也用于流程干预", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "myActionId", value = "待办记录ID", required = true, dataType = "Long"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/recall", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> recall(HttpServletRequest request, @RequestParam(defaultValue = "-1") Long myActionId) {
        WorkflowMgr wfm = new WorkflowMgr();
        boolean re;
        try {
            re = wfm.recallMyAction(request, myActionId);
        } catch (ErrMsgException e) {
            LogUtil.getLog(getClass()).error(e);
            return new Result<>(false, e.getMessage());
        }
        return new Result<>(re);
    }

    @ApiOperation(value = "回复", notes = "回复", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "myActionId", value = "待办记录ID", required = false, dataType = "Long"),
            @ApiImplicitParam(name = "parentId", value = "被回复的ID", required = false, dataType = "Long"),
            @ApiImplicitParam(name = "content", value = "回复内容", required = true, dataType = "String"),
            @ApiImplicitParam(name = "isSecret", value = "是否私密", required = false, dataType = "Integer"),
            @ApiImplicitParam(name = "progress", value = "进度", required = false, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/addReply", produces = {"application/json;charset=UTF-8;"})
    public Result<Object> addReply(@RequestParam(value="flowId")Integer flowId, @RequestParam(value="myActionId", defaultValue = "-1") Long myActionId, @RequestParam(value="parentId", defaultValue = "-1")Long parentId, @RequestParam(value="content", required = true) String content, @RequestParam(value="isSecret", defaultValue = "0") Integer isSecret, @RequestParam(value="progress", defaultValue = "0") Integer progress, MultipartFile[] files) {
        WorkflowAnnexMgr workflowAnnexMgr = new WorkflowAnnexMgr();
        String userName = authUtil.getUserName();
        String userRealName = userCache.getUser(userName).getRealName();
        boolean re = true;
        String replyName = "";
        long id = SequenceManager.nextID(SequenceManager.OA_FLOW_ANNEX);
        long actionId = 0;
        // 详情页回复时 myActionId为-1
        if (myActionId != -1){
            MyActionDb mad = new MyActionDb(myActionId);
            mad = mad.getMyActionDb(myActionId);
            actionId = mad.getActionId();
            replyName = mad.getUserName();
        }
        if (parentId != -1) {
            WorkflowAnnexDb workflowAnnexDb = new WorkflowAnnexDb();
            workflowAnnexDb = (WorkflowAnnexDb)workflowAnnexDb.getQObjectDb(parentId);
            replyName = workflowAnnexDb.getString("user_name");
        }

        WorkflowDb wf = new WorkflowDb();
        wf = wf.getWorkflowDb(flowId);

        Object[] ary = workflowService.parseAtUsers(content);
        List<String> list = (List<String>) ary[0];
        content = ary[1].toString();

        String sql = "insert into flow_annex (id,flow_id,content,user_name,reply_name,add_date,action_id,is_secret,parent_id,progress) values(?,?,?,?,?,?,?,?,?,?)";
        re = workflowAnnexMgr.create(sql, new Object[]{id, flowId, content, userName, replyName, new Date(), actionId, isSecret, parentId, progress});

        if (re) {
            if (files != null && files.length > 0) {
                Calendar cal = Calendar.getInstance();
                int year = cal.get(Calendar.YEAR);
                int month = cal.get(Calendar.MONTH) + 1;
                com.redmoon.oa.Config cfg = com.redmoon.oa.Config.getInstance();
                String vpath = cfg.get("file_flow") + "/" + year + "/" + month;

                for (MultipartFile file : files) {
                    String ext = StrUtil.getFileExt(file.getOriginalFilename());
                    // 使用随机名称写入磁盘
                    String diskName = FileUpload.getRandName() + "." + ext;
                    try {
                        fileService.write(file, vpath, diskName);
                    } catch (IOException e) {
                        LogUtil.getLog(getClass()).error(e);
                    }

                    WorkflowAnnexAttachment wfaa = new WorkflowAnnexAttachment();
                    wfaa.setAnnexId(id);
                    wfaa.setVisualPath(vpath);
                    wfaa.setName(file.getOriginalFilename());
                    wfaa.setDiskName(diskName);
                    wfaa.setOrders(0);
                    wfaa.setSize(file.getSize());
                    wfaa.create();
                }
            }

            if (!list.isEmpty()) {
                PaperDistributeDb pdd = new PaperDistributeDb();
                String fromUnit = authUtil.getUserUnitCode();
                for (String uName : list) {
                    long paperId = SequenceManager.nextID(SequenceManager.OA_FLOW_PAPER_DISTRIBUTE);
                    try {
                        pdd.create(new JdbcTemplate(), new Object[]{paperId, wf.getTitle(), flowId, fromUnit, uName, new Date(), userName, 1, 0, PaperDistributeDb.KIND_USER, actionId, PaperDistributeDb.OP_TYPE_AT});
                    } catch (ResKeyException e) {
                        LogUtil.getLog(getClass()).error(e.getMessage(request));
                    }
                }
            }

            // 写入进度
            Leaf lf = new Leaf();
            lf = lf.getLeaf(wf.getTypeCode());
            String formCode = lf.getFormCode();
            FormDb fd = new FormDb();
            fd = fd.getFormDb(formCode);
            // 进度为0的时候不更新
            if (fd.isProgress() && progress > 0) {
                com.redmoon.oa.flow.FormDAO fdao = new com.redmoon.oa.flow.FormDAO();
                fdao = fdao.getFormDAOByCache(flowId, fd);
                fdao.setCwsProgress(progress);
                fdao.save();
            }

            MessageDb md = new MessageDb();
            String myAction = "action=" + MessageDb.ACTION_FLOW_SHOW + "|flowId=" + flowId;

            if (isSecret != 0) {//如果是隐藏“评论”，只提醒发起“意见”的人
                if (!replyName.equals(userName)) {//如果发起“意见”的人不是自己，就提醒
                    md.sendSysMsg(replyName, "请注意查看我的流程：" + wf.getTitle(), userRealName + "发表了评论：<p>" + content + "</p>", myAction);
                }
            } else {
                //如果不是隐藏“评论”，提醒所有参与流程的人
                //解析得到参与流程的所有人
                StringBuilder sb = new StringBuilder();
                MyActionDb mad = new MyActionDb();
                Vector<MyActionDb> v = mad.getMyActionDbOfFlow(flowId);
                for (MyActionDb myActionDb : v) {
                    StrUtil.concat(sb, ",", myActionDb.getUserName());
                }
                String[] partakeUsersArr = StrUtil.split(sb.toString(), ",");
                for (String user : partakeUsersArr) {
                    //如果不是自己就提醒
                    if (!user.equals(userName)) {
                        md.sendSysMsg(user, "请注意查看我的流程：" + wf.getTitle(), userRealName + "发表了评论：<p>" + content + "</p>", myAction);
                    }
                }
            }
        }

        return new Result<>(re);
    }

    @ApiOperation(value = "提交流程", notes = "提交流程", httpMethod = "POST")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/finishAction", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public String finishAction(HttpServletRequest request) {
        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        try {
            // 将场景置为流程
            threadContext.setSceneFlow();
            json = workflowService.finishActionConditional(request, new com.redmoon.oa.pvg.Privilege());
            if (json.containsKey("ret") && "0".equals(json.getString("ret"))) {
                json.put("code", CommonConstant.SC_INTERNAL_SERVER_ERROR_500);
            } else {
                json.put("code", CommonConstant.SC_OK_200);
            }

            // 自动存档
            if ("AutoSaveArchiveNodeCommit".equals(json.getString("operate"))) {
                WorkflowMgr wfm = new WorkflowMgr();
                WorkflowDb wf = new WorkflowDb();
                wf = wf.getWorkflowDb(json.getIntValue("flowId"));
                WorkflowActionDb wa = new WorkflowActionDb();
                wa = wa.getWorkflowActionDb(json.getIntValue("actionId"));
                wfm.autoSaveArchive(request, wf, wa);
            }
        } catch (ErrMsgException e) {
            // LogUtil.getLog(getClass()).error(e);
            // 置为异常状态
            threadContext.setAbnormal(true);
            json.put("ret", 0);
            json.put("code", CommonConstant.SC_INTERNAL_SERVER_ERROR_500);
            json.put("msg", e.getMessage());
            json.put("op", "Exception occured in finishAction");
        } catch (ClassCastException | NullPointerException | IllegalArgumentException e) {
            LogUtil.getLog(getClass()).error(e);
            // 置为异常状态
            threadContext.setAbnormal(true);
            json.put("ret", 0);
            json.put("code", CommonConstant.SC_INTERNAL_SERVER_ERROR_500);
            json.put("msg", e.getMessage());
            json.put("op", "Exception occured in finishAction");
        } finally {
            // 清缓存
            threadContext.remove();
        }
        return json.toString();
    }

    @ApiOperation(value = "提交自由流程", notes = "提交自由流程", httpMethod = "POST")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/finishActionFree", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public String finishActionFree(HttpServletRequest request) {
        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        try {
            // 将场景置为流程
            threadContext.setSceneFlow();
            json = workflowService.finishActionFreeConditional(request, new com.redmoon.oa.pvg.Privilege());
        } catch (ErrMsgException | ClassCastException | NullPointerException | IllegalArgumentException e) {
            // 置为异常状态
            threadContext.setAbnormal(true);
            json.put("ret", 0);
            json.put("msg", e.getMessage());
            json.put("op", "");
            LogUtil.getLog(getClass()).error(e);
        } finally {
            // 清缓存
            threadContext.remove();
        }
        return json.toString();
    }

    @ApiOperation(value = "收藏流程", notes = "收藏流程", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/favorite", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> favorite(@RequestParam(value="flowId", required = true) Integer flowId) {
        boolean re;
        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        WorkflowFavoriteDb wfd = new WorkflowFavoriteDb();
        try {
            if (wfd.isExist(authUtil.getUserName(), flowId)) {
                String str = i18nUtil.get("processBeConcerned");
                throw new ErrMsgException(str);
            }
            re = wfd.create(new JdbcTemplate(), new Object[]{flowId, authUtil.getUserName(), new java.util.Date(), 0});
        } catch (ErrMsgException | ResKeyException e) {
            json.put("res", 1);
            json.put("msg", e.getMessage().replace("\\r", "<BR />"));
            return new Result<>(json);
        }
        if (re) {
            json.put("res", 0);
            String str = i18nUtil.get("info_op_success");
            json.put("msg", str);
        } else {
            json.put("res", 1);
            String str = i18nUtil.get("info_op_fail");
            json.put("msg", str);
        }
        return new Result<>(json);
    }

    @ApiOperation(value = "取消收藏流程", notes = "取消收藏流程", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/unfavorite", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> unfavorite(@RequestParam(value="flowId", required = true) Integer flowId) {
        boolean re;
        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        WorkflowFavoriteDb wfd = new WorkflowFavoriteDb();
        try {
            wfd = wfd.getWorkflowFavoriteDb(authUtil.getUserName(), flowId);
            if (wfd != null) {
                re = wfd.del();
            } else {
                String str = i18nUtil.get("notAlreadyExist");
                throw new ErrMsgException(str);
            }
        } catch (ErrMsgException | ResKeyException e) {
            json.put("res", 1);
            json.put("msg", e.getMessage().replace("\\r", "<BR />"));
            // LogUtil.getLog(getClass()).error(e);
            return new Result<>(json);
        }
        if (re) {
            json.put("res", 0);
            String str = i18nUtil.get("info_op_success");
            json.put("msg", str);
        } else {
            json.put("res", 1);
            String str = i18nUtil.get("info_op_fail");
            json.put("msg", str);
        }
        return new Result<>(json);
    }

    @ApiOperation(value = "批量放弃流程", notes = "批量放弃流程", httpMethod = "POST")
    @ResponseBody
    @RequestMapping(value = "/flow/discardFlowBatch", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public String discardFlowBatch(HttpServletRequest request) {
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        String ids = ParamUtil.get(request, "ids");
        String[] ary = StrUtil.split(ids, ",");
        String userName = privilege.getUser(request);
        for (int i = 0; i < ary.length; i++) {
            int flowId = StrUtil.toInt(ary[i], -1);
            WorkflowDb wf = new WorkflowDb();
            wf = wf.getWorkflowDb(flowId);
            try {
                wf.discard(userName);
            } catch (ErrMsgException e) {
                LogUtil.getLog(getClass()).error(e);
                json.put("ret", "0");
                json.put("msg", e.getMessage().replace("\\r", "<BR />"));
                return json.toString();
            }
        }

        json.put("ret", "1");
        String str = i18nUtil.get("info_op_success");
        json.put("msg", str);
        return json.toString();
    }

    @ApiOperation(value = "调试模式中配置属性", notes = "调试模式中配置属性", httpMethod = "POST")
    @ResponseBody
    @RequestMapping(value = "/flow/applyProps", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> applyProps(HttpServletRequest request) {
        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        String fieldWrite = ParamUtil.get(request, "fieldWrite");
        String fieldHide = ParamUtil.get(request, "fieldHide");
        int flowId = ParamUtil.getInt(request, "flowId", -1);
        int actionId = ParamUtil.getInt(request, "actionId", -1);

        WorkflowDb wf = new WorkflowDb();
        wf = wf.getWorkflowDb(flowId);

        WorkflowPredefineDb wpd = new WorkflowPredefineDb();
        wpd = wpd.getDefaultPredefineFlow(wf.getTypeCode());
        WorkflowActionDb wad = new WorkflowActionDb();
        wad = wad.getWorkflowActionDb(actionId);

        WorkflowDb wfDefault = new WorkflowDb();
        wfDefault.setFlowString(wpd.getFlowString());
        wfDefault.setFlowJson(wpd.getFlowJson());

        WorkflowActionDb waDefault = null;

        try {
            java.util.Vector v = wfDefault.getActionsFromString(wfDefault.getFlowString());
            java.util.Iterator ir = v.iterator();
            while (ir.hasNext()) {
                WorkflowActionDb wa = (WorkflowActionDb) ir.next();
                if (wa.getInternalName().equals(wad.getInternalName())) {
                    wa.setFieldWrite(fieldWrite);
                    wa.setFieldHide(fieldHide);

                    String item2 = wa.generateItem2();
                    wa.setItem2(item2);

                    waDefault = wa;
                    break;
                }
            }

            if (waDefault != null) {
                wfDefault.renewWorkflowString(waDefault, false);

                wpd.setFlowString(wfDefault.getFlowString());
                DebugUtil.i(getClass(), "applyProps", wfDefault.getFlowJson());
                wpd.setFlowJson(wfDefault.getFlowJson());
                wpd.save();
            }
            wf.refreshFlow();
        } catch (ErrMsgException e) {
            LogUtil.getLog(getClass()).error(e);
            return new Result<>(false, e.getMessage());
        }

        json.put("ret", "1");
        String str = LocalUtil.LoadString(request, "info_op_success");
        json.put("msg", str);
        return new Result<>(true);
    }

    @ApiOperation(value = "运行节点上的验证事件", notes = "运行节点上的验证事件", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "actionId", value = "节点ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/runValidateScript", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> runValidateScript(HttpServletRequest request, @RequestParam(defaultValue = "-1") Integer flowId,  @RequestParam(defaultValue = "-1") Integer actionId) {
        FormDAOMgr fdm = new FormDAOMgr();
        BSHShell shell = null;
        try {
            shell = fdm.runValidateScript(request, flowId, actionId);
            if (shell == null) {
                return new Result<>("请检查脚本是否存在");
            } else {
                String errDesc = shell.getConsole().getLogDesc();
                return new Result<>(errDesc);
            }
        } catch (ErrMsgException e) {
            String errDesc = "";
            if (shell != null) {
                errDesc = shell.getConsole().getLogDesc();
            }
            if (!"".equals(errDesc)) {
                errDesc += "\r\n" + e.getMessage();
            } else {
                errDesc = e.getMessage();
            }
            return new Result<>(errDesc);
        }
    }

    @ApiOperation(value = "运行流程结束事件", notes = "运行流程结束事件", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "actionId", value = "节点ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/runFinishScript", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> runFinishScript(HttpServletRequest request, @RequestParam(value = "flowId") Integer flowId, @RequestParam(value = "actionId") Integer actionId) throws ErrMsgException {
        String re;
        try {
            re = workflowService.runFinishScript(request, flowId, actionId);
        } catch (ErrMsgException e) {
            re = e.getMessage();
        }
        return new Result<>(re);
    }

    @ApiOperation(value = "调用办理记录所在节点上的流转事件", notes = "调用办理记录所在节点上的流转事件，用于流程干预", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "myActionId", value = "办理记录ID", required = true, dataType = "Long"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/runDeliverScript", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> runDeliverScript(@RequestParam(value = "flowId") Integer flowId, @RequestParam(value = "myActionId")Long myActionId) throws ErrMsgException {
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        BSHShell shell = null;

        WorkflowDb wf = new WorkflowDb();
        wf = wf.getWorkflowDb(flowId);

        Leaf lf = new Leaf();
        lf = lf.getLeaf(wf.getTypeCode());
        FormDb fd = new FormDb();
        fd = fd.getFormDb(lf.getFormCode());

        FormDAO fdao = new FormDAO();
        fdao = fdao.getFormDAOByCache(flowId, fd);

        MyActionDb mad = new MyActionDb();
        mad = mad.getMyActionDb(myActionId);

        WorkflowActionDb wa = new WorkflowActionDb();
        wa = wa.getWorkflowActionDb((int) mad.getActionId());

        WorkflowPredefineDb wpd = new WorkflowPredefineDb();
        wpd = wpd.getDefaultPredefineFlow(wf.getTypeCode());
        WorkflowPredefineMgr wpm = new WorkflowPredefineMgr();
        String script = wpm.getActionFinishScript(wpd.getScripts(), wa.getInternalName());

        if (script != null && !"".equals(script.trim())) {
            WorkflowMgr wm = new WorkflowMgr();
            shell = wm.runDeliverScript(request, privilege.getUser(request), wf, fdao, myActionId, script, true);
        }

        if (shell == null) {
            return new Result<>("请检查脚本是否存在");
        } else {
            String errDesc = shell.getConsole().getLogDesc().trim();
            if ("".equals(errDesc)) {
                errDesc = "操作成功";
            }
            // json.put("msg", StrUtil.toHtml(errDesc));
            return new Result<>(errDesc);
        }
    }

    @ApiOperation(value = "运行节点上的退回事件", notes = "运行节点上的退回事件", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "actionId", value = "节点ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/runReturnScript", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> runReturnScript(@RequestParam(value = "flowId") Integer flowId, @RequestParam(value = "actionId")Integer actionId) throws ErrMsgException {
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        BSHShell shell = null;

        WorkflowDb wf = new WorkflowDb();
        wf = wf.getWorkflowDb(flowId);

        Leaf lf = new Leaf();
        lf = lf.getLeaf(wf.getTypeCode());
        FormDb fd = new FormDb();
        fd = fd.getFormDb(lf.getFormCode());

        FormDAO fdao = new FormDAO();
        fdao = fdao.getFormDAOByCache(flowId, fd);

        WorkflowActionDb wa = new WorkflowActionDb();
        wa = wa.getWorkflowActionDb(actionId);

        WorkflowPredefineDb wpd = new WorkflowPredefineDb();
        wpd = wpd.getDefaultPredefineFlow(wf.getTypeCode());
        WorkflowPredefineMgr wpm = new WorkflowPredefineMgr();
        String script = wpm.getActionReturnScript(wpd.getScripts(), wa.getInternalName());

        if (script != null && !"".equals(script.trim())) {
            WorkflowMgr wm = new WorkflowMgr();
            shell = wm.runReturnScript(request, privilege.getUser(request), wf.getId(), fdao, script);
        }

        if (shell == null) {
            return new Result<>("请检查脚本是否存在");
        } else {
            String errDesc = shell.getConsole().getLogDesc().trim();
            if ("".equals(errDesc)) {
                errDesc = "操作成功";
            }
            return new Result<>(errDesc);
        }
    }

    @ApiOperation(value = "运行节点上的被退回事件", notes = "运行节点上的被退回事件", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "actionId", value = "节点ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/runReturnedScript", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> runReturnedScript(@RequestParam(value = "flowId") Integer flowId, @RequestParam(value = "actionId")Integer actionId) throws ErrMsgException {
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        BSHShell shell = null;

        WorkflowDb wf = new WorkflowDb();
        wf = wf.getWorkflowDb(flowId);

        Leaf lf = new Leaf();
        lf = lf.getLeaf(wf.getTypeCode());
        FormDb fd = new FormDb();
        fd = fd.getFormDb(lf.getFormCode());

        FormDAO fdao = new FormDAO();
        fdao = fdao.getFormDAOByCache(flowId, fd);

        WorkflowActionDb wa = new WorkflowActionDb();
        wa = wa.getWorkflowActionDb(actionId);

        WorkflowPredefineDb wpd = new WorkflowPredefineDb();
        wpd = wpd.getDefaultPredefineFlow(wf.getTypeCode());
        WorkflowPredefineMgr wpm = new WorkflowPredefineMgr();
        String script = wpm.getActionReturnedScript(wpd.getScripts(), wa.getInternalName());

        if (script != null && !"".equals(script.trim())) {
            WorkflowMgr wm = new WorkflowMgr();
            shell = wm.runReturnedScript(request, privilege.getUser(request), wf.getId(), fdao, script);
        }

        if (shell == null) {
            return new Result<>("请检查脚本是否存在");
        } else {
            String errDesc = shell.getConsole().getLogDesc().trim();
            if ("".equals(errDesc)) {
                errDesc = "操作成功";
            }
            return new Result<>(errDesc);
        }
    }

    @ApiOperation(value = "恢复流程", notes = "恢复流程", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/recover", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> recover(@RequestParam Integer flowId) {
        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();

        WorkflowDb wf = new WorkflowDb();
        wf = wf.getWorkflowDb(flowId);
        wf.setStatus(WorkflowDb.STATUS_STARTED);
        wf.save();

        // 其对应台帐记录的状态也得恢复为处理中
        Leaf lf = new Leaf();
        lf = lf.getLeaf(wf.getTypeCode());
        FormDAO dao = (FormDAO)flowFormDaoCache.getFormDao(wf.getId(), lf.getFormCode());
        if (dao != null) {
            dao.setStatus(FormDAO.STATUS_NOT);
            boolean re = dao.save();
            return new Result<>(re);
        } else {
            return new Result<>(false, "记录已不存在");
        }
    }

    @ApiOperation(value = "连接器日志列表", notes = "连接器日志列表", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/listConnectorLog", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> listConnectorLog(@RequestParam(value = "flowId") Integer flowId) {
        Map<String, Object> map = new HashMap<>();
        map.put("flow_id", flowId);
        com.redmoon.oa.visual.FormDAO fdao = new com.redmoon.oa.visual.FormDAO();
        List<com.redmoon.oa.visual.FormDAO> list = fdao.selectList("connector_run_log", map);

        JSONArray jsonArray = new JSONArray();
        for (com.redmoon.oa.visual.FormDAO formDAO : list) {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("runTime", formDAO.getFieldValue("run_time"));
            jsonObject.put("connectorId", formDAO.getFieldValue("connector"));
            jsonObject.put("connectorName", formDAO.getFieldValue("connector_name"));
            jsonObject.put("connectorActionId", formDAO.getFieldValue("connector_action"));
            jsonObject.put("connectionActionName", formDAO.getFieldValue("connector_action_name"));
            jsonObject.put("result", formDAO.getFieldValue("result"));
            jsonArray.add(jsonObject);
        }
        return new Result<>(jsonArray);
    }

    @ApiOperation(value = "回复列表", notes = "回复列表", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/listAnnex", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> listAnnex(@RequestParam(value = "flowId") Integer flowId) {
        com.alibaba.fastjson.JSONArray aryAnnex = new com.alibaba.fastjson.JSONArray();
        WorkflowAnnexDb wad = new WorkflowAnnexDb();
        WorkflowAnnexAttachment workflowAnnexAttachment = new WorkflowAnnexAttachment();
        Vector<WorkflowAnnexDb> vec1 = wad.listRoot(flowId, authUtil.getUserName());
        for (WorkflowAnnexDb workflowAnnexDb : vec1) {
            wad = workflowAnnexDb;

            com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
            json.put("realName", userCache.getUser(wad.getString("user_name")).getRealName());
            json.put("process", wad.getInt("progress"));
            json.put("content", wad.getString("content"));
            json.put("id", wad.getLong("id"));
            json.put("addDate", DateUtil.format(wad.getDate("add_date"), "yyyy-MM-dd HH:mm:ss"));
            json.put("flowId", wad.getInt("flow_id"));
            json.put("actionId", wad.getInt("action_id"));

            if (wad.getInt("action_id") > 0) {
                WorkflowActionDb workflowActionDb = new WorkflowActionDb();
                workflowActionDb = workflowActionDb.getWorkflowActionDb(wad.getInt("action_id"));
                json.put("actionTitle", workflowActionDb.getTitle());
                json.put("actionColorIndex", workflowActionDb.getOfficeColorIndex());
            }

            json.put("userName", wad.getString("user_name"));
            json.put("parentId", wad.getLong("parent_id"));
            json.put("attachments", workflowAnnexAttachment.getAttachments(wad.getLong("id")));
            aryAnnex.add(json);

            WorkflowAnnexDb wad2 = new WorkflowAnnexDb();
            com.alibaba.fastjson.JSONArray aryAnnexSub = new com.alibaba.fastjson.JSONArray();
            Vector<WorkflowAnnexDb> vec2 = wad2.listChildren(wad.getInt("id"), authUtil.getUserName());
            for (WorkflowAnnexDb annexDb : vec2) {
                wad2 = annexDb;
                int id2 = (int) wad2.getLong("id");

                com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject();
                jsonObject.put("id", id2);
                jsonObject.put("userName", wad2.getString("user_name"));
                jsonObject.put("realName", userCache.getUser(wad2.getString("user_name")).getRealName());
                jsonObject.put("content", wad2.getString("content"));
                jsonObject.put("addDate", DateUtil.format(wad2.getDate("add_date"), "yyyy-MM-dd HH:mm"));
                jsonObject.put("flowId", wad2.getString("flow_id"));
                jsonObject.put("actionId", wad2.getString("action_id"));
                if (wad2.getInt("action_id") > 0) {
                    WorkflowActionDb workflowActionDb = new WorkflowActionDb();
                    workflowActionDb = workflowActionDb.getWorkflowActionDb(wad2.getInt("action_id"));
                    jsonObject.put("actionTitle", workflowActionDb.getTitle());
                    jsonObject.put("actionColorIndex", workflowActionDb.getOfficeColorIndex());
                } else {
                    jsonObject.put("actionColorIndex", 1);
                }
                jsonObject.put("replyRealName", userCache.getUser(wad2.getString("reply_name")).getRealName());
                jsonObject.put("parentId", wad2.getLong("parent_id"));
                jsonObject.put("attachments", workflowAnnexAttachment.getAttachments(id2));
                aryAnnexSub.add(jsonObject);
            }
            json.put("aryAnnexSub", aryAnnexSub);
        }
        return new Result<>(aryAnnex);
    }

    @ApiOperation(value = "删除回复", notes = "删除回复", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "回复ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/delAnnex", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> delAnnex(@RequestParam(value = "id") Integer id) {
        WorkflowAnnexDb wad = new WorkflowAnnexDb();
        wad = (WorkflowAnnexDb) wad.getQObjectDb(id);

        boolean isValid = false;
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        if (privilege.isUserPrivValid(request, "admin")) {
            isValid = true;
        }
        else {
            String annexUserName = wad.getString("user_name");
            if (annexUserName.equals(authUtil.getUserName())) {
                isValid = true;
            } else {
                int flowId = wad.getInt("flow_id");
                WorkflowDb wf = new WorkflowDb();
                wf = wf.getWorkflowDb(flowId);
                LeafPriv lp = new LeafPriv(wf.getTypeCode());
                if (privilege.isUserPrivValid(request, "admin.flow")) {
                    if (lp.canUserExamine(privilege.getUser(request))) {
                        isValid = true;
                    }
                }
            }
        }
        if (isValid) {
            return new Result<>(false, i18nUtil.get("pvg_invalid"));
        }

        boolean re;
        try {
            re = wad.del();
            if (re) {
                WorkflowAnnexAttachment wfaa = new WorkflowAnnexAttachment();
                wfaa.delAttachments(wad.getLong("id"));
            }
        } catch (ResKeyException e) {
            return new Result<>(false, e.getMessage(request));
        }
        return new Result<>(re);
    }

    @ApiOperation(value = "删除回复中的附件", notes = "删除回复中的附件", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "回复中的附件ID", required = true, dataType = "Long"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/delAnnexAttachment", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> delAnnexAttachment(@RequestParam(value = "id") Long id) {
        // 如果是流程管理员、留言创建者，则能够删除附件
        boolean isValid = false;
        WorkflowAnnexAttachment wfaa = new WorkflowAnnexAttachment(id);
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        if (privilege.isUserPrivValid(request, "admin")) {
            isValid = true;
        }
        else {
            long annexId = wfaa.getAnnexId();
            WorkflowAnnexDb wad = new WorkflowAnnexDb();
            wad = (WorkflowAnnexDb) wad.getQObjectDb(annexId);
            String annexUserName = wad.getString("user_name");
            if (annexUserName.equals(authUtil.getUserName())) {
                isValid = true;
            } else {
                int flowId = wad.getInt("flow_id");
                WorkflowDb wf = new WorkflowDb();
                wf = wf.getWorkflowDb(flowId);
                LeafPriv lp = new LeafPriv(wf.getTypeCode());
                if (privilege.isUserPrivValid(request, "admin.flow")) {
                    if (lp.canUserExamine(privilege.getUser(request))) {
                        isValid = true;
                    }
                }
            }
        }
        if (isValid) {
            return new Result<>(false, i18nUtil.get("pvg_invalid"));
        }

        return new Result<>(wfaa.del());
    }

    @ApiOperation(value = "创建嵌套表中的记录", notes = "创建嵌套表中的记录", httpMethod = "POST")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/createNestSheetRelated", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<com.alibaba.fastjson.JSONObject> createNestSheetRelated() {
        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        try {
            // 将场景置为流程
            threadContext.setSceneFlow();
            json = workflowService.createNestSheetRelatedConditional(request);
        }
        catch (ErrMsgException | ClassCastException | NullPointerException | IllegalArgumentException e) {
            // 置为异常状态
            threadContext.setAbnormal(true);
            LogUtil.getLog(getClass()).error(e);
            json.put("res", 1);
            json.put("msg", e.getMessage());
            json.put("op", "");
        }
        finally {
            // 清缓存
            threadContext.remove();
        }
        return new Result<>(json);
    }

    @ApiOperation(value = "修改嵌套表中的记录", notes = "修改嵌套表中的记录", httpMethod = "POST")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/updateNestSheetRelated", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<com.alibaba.fastjson.JSONObject> updateNestSheetRelated() {
        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        try {
            // 将场景置为流程
            threadContext.setSceneFlow();
            json = workflowService.updateNestSheetRelatedConditional(request);
        }
        catch (ErrMsgException | ClassCastException | NullPointerException | IllegalArgumentException e) {
            // 置为异常状态
            threadContext.setAbnormal(true);
            json.put("res", 1);
            json.put("msg", e.getMessage());
        }
        finally {
            // 清缓存
            threadContext.remove();
        }
        return new Result<>(json);
    }

    @ApiOperation(value = "删除嵌套表中的附件", notes = "删除嵌套表中的附件", httpMethod = "POST")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/delNestSheetRelated", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public String delNestSheetRelated() {
        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        String formCodeRelated = ParamUtil.get(request, "formCodeRelated");

        com.redmoon.oa.Config cfg = new com.redmoon.oa.Config();
        boolean isNestSheetCheckPrivilege = cfg.getBooleanProperty("isNestSheetCheckPrivilege");

        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        ModulePrivDb mpd = new ModulePrivDb(formCodeRelated);
        if (isNestSheetCheckPrivilege && !mpd.canUserManage(privilege.getUser(request))) {
            json.put("ret", "0");
            json.put("msg", cn.js.fan.web.SkinUtil.LoadString(request, "pvg_invalid"));
            return json.toString();
        }

        String moduleCode = ParamUtil.get(request, "moduleCode");
        if ("".equals(moduleCode)) {
            moduleCode = formCodeRelated;
        }

        FormMgr fm = new FormMgr();
        FormDb fdRelated = fm.getFormDb(formCodeRelated);
        com.redmoon.oa.visual.FormDAOMgr fdm = new com.redmoon.oa.visual.FormDAOMgr(fdRelated);
        try {
            boolean isNestSheet = true;
            if (fdm.del(request, isNestSheet, moduleCode)) {
                json.put("ret", "1");
                json.put("msg", "操作成功！");
            } else {
                json.put("ret", "0");
                json.put("msg", "操作失败！");
            }
        } catch (ErrMsgException e) {
            json.put("ret", "0");
            json.put("msg", e.getMessage());
            // LogUtil.getLog(getClass()).error(e);
        }
        return json.toString();
    }

    @ApiOperation(value = "删除嵌套表中的附件", notes = "删除嵌套表中的附件", httpMethod = "POST")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/delAttachForNestSheetRelated", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public String delAttachForNestSheetRelated() {
        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        String formCode = ParamUtil.get(request, "formCode"); // 主模块编码
        if ("".equals(formCode)) {
            json.put("ret", "0");
            json.put("msg", "编码不能为空！");
            return json.toString();
        }

        String formCodeRelated = ParamUtil.get(request, "formCodeRelated"); // 从模块编码

        com.redmoon.oa.Config cfg = new com.redmoon.oa.Config();
        boolean isNestSheetCheckPrivilege = cfg.getBooleanProperty("isNestSheetCheckPrivilege");
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        ModulePrivDb mpd = new ModulePrivDb(formCodeRelated);
        if (isNestSheetCheckPrivilege && !mpd.canUserManage(privilege.getUser(request))) {
            json.put("ret", "0");
            json.put("msg", cn.js.fan.web.SkinUtil.LoadString(request, "pvg_invalid"));
            return json.toString();
        }

        long attachId = ParamUtil.getLong(request, "attachId", -1);
        if (attachId == -1) {
            json.put("ret", "0");
            json.put("msg", SkinUtil.LoadString(request, "err_id"));
            return json.toString();
        }

        boolean re = false;
        com.redmoon.oa.visual.Attachment att = new com.redmoon.oa.visual.Attachment(attachId);
        if (att.isLoaded()) {
            re = att.del();
        }
        if (re) {
            json.put("ret", "1");
            json.put("msg", "操作成功！");
        } else {
            json.put("ret", "0");
            json.put("msg", "操作失败！");
        }
        return json.toString();
    }

    @ApiOperation(value = "匹配分支及分支上的用户", notes = "匹配分支及分支上的用户", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "myActionId", value = "待办记录ID", required = true, dataType = "Long"),
            @ApiImplicitParam(name = "actionId", value = "流程节点ID", required = false, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/matchBranchAndUser", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<com.alibaba.fastjson.JSONObject> matchBranchAndUser(@RequestParam(value = "myActionId", required = false) Long myActionId, @RequestParam(value = "actionId", required = false) Integer actionId) {
        try {
            return new Result<>(workflowService.matchBranchAndUser(request, myActionId, actionId));
        } catch (ErrMsgException e) {
            LogUtil.getLog(getClass()).error(e);
            return new Result<>(false, e.getMessage());
        }
    }

    @ApiOperation(value = "转办", notes = "转办", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "myActionId", value = "待办记录ID", required = true, dataType = "Long"),
            @ApiImplicitParam(name = "toUserName", value = "转办的用户名", required = false, dataType = "String"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/transfer", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> transfer(@RequestParam(value = "myActionId") Long myActionId, @RequestParam(value = "toUserName") String toUserName) {
        MyActionDb myActionDb = new MyActionDb();
        myActionDb = myActionDb.getMyActionDb(myActionId);
        if (myActionDb.getCheckStatus()==MyActionDb.CHECK_STATUS_PASS) {
            return new Result<>(false, i18nUtil.get("nodeByOtherPersonal"));
        }

        WorkflowActionDb wa = new WorkflowActionDb();
        int actionId = (int)myActionDb.getActionId();
        wa = wa.getWorkflowActionDb(actionId);
        if ( wa==null || !wa.isLoaded()) {
            return new Result<>(false, i18nUtil.get("actionNotExist"));
        }
        int flowId = wa.getFlowId();
        WorkflowMgr wfm = new WorkflowMgr();
        WorkflowDb wf = wfm.getWorkflowDb(flowId);

        WorkflowPredefineDb wfp = new WorkflowPredefineDb();
        wfp = wfp.getPredefineFlowOfFree(wf.getTypeCode());

        if (wa.getStatus()== WorkflowActionDb.STATE_DOING || wa.getStatus()== WorkflowActionDb.STATE_RETURN) {
            ;
        } else {
            // 有可能会是重激活的情况
            if (!wfp.isReactive()) {
                String str = i18nUtil.get("processStatus");
                String str1 = i18nUtil.get("mayHaveBeenProcess");
                return new Result<>(false, str + wa.getStatus() + "，"+str1);
            }
        }

        try {
            wfm.transfer(request, myActionId, toUserName);
        }
        catch (ErrMsgException e) {
            return new Result<>(false, e.getMessage());
        }
        return new Result<>();
    }

    @ApiOperation(value = "挂起流程", notes = "挂起流程", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "myActionId", value = "待办记录的ID", required = true, dataType = "Long"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/suspend", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> suspend(@RequestParam(value = "myActionId") Long myActionId) {
        MyActionDb myActionDb = new MyActionDb();
        myActionDb = myActionDb.getMyActionDb(myActionId);
        if (myActionDb.getCheckStatus()==MyActionDb.CHECK_STATUS_PASS) {
            return new Result<>(false, i18nUtil.get("nodeByOtherPersonal"));
        }

        WorkflowActionDb wa = new WorkflowActionDb();
        int actionId = (int)myActionDb.getActionId();
        wa = wa.getWorkflowActionDb(actionId);
        if ( wa==null || !wa.isLoaded()) {
            return new Result<>(false, i18nUtil.get("actionNotExist"));
        }
        int flowId = wa.getFlowId();
        WorkflowMgr wfm = new WorkflowMgr();
        WorkflowDb wf = wfm.getWorkflowDb(flowId);

        WorkflowPredefineDb wfp = new WorkflowPredefineDb();
        wfp = wfp.getPredefineFlowOfFree(wf.getTypeCode());

        if (wa.getStatus()== WorkflowActionDb.STATE_DOING || wa.getStatus()== WorkflowActionDb.STATE_RETURN) {
            ;
        } else {
            // 有可能会是重激活的情况
            if (!wfp.isReactive()) {
                return new Result<>(false, i18nUtil.get("processStatus") + wa.getStatus() + ", "+i18nUtil.get("mayHaveBeenProcess"));
            }
        }

        try {
            wfm.suspend(request, myActionId);
        }
        catch (ErrMsgException e) {
            return new Result<>(false, e.getMessage());
        }
        return new Result<>(true);
    }

    @ApiOperation(value = "恢复流程", notes = "恢复流程", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "myActionId", value = "待办记录的ID", required = true, dataType = "Long"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/resume", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> resume(@RequestParam(value = "myActionId", required = true) Long myActionId) {
        MyActionDb myActionDb = new MyActionDb();
        myActionDb = myActionDb.getMyActionDb(myActionId);
        if (myActionDb.getCheckStatus() == MyActionDb.CHECK_STATUS_PASS) {
            return new Result<>(false, i18nUtil.get("nodeByOtherPersonal"));
        }

        WorkflowActionDb wa = new WorkflowActionDb();
        int actionId = (int)myActionDb.getActionId();
        wa = wa.getWorkflowActionDb(actionId);
        if ( wa==null || !wa.isLoaded()) {
            return new Result<>(false, i18nUtil.get("actionNotExist"));
        }
        int flowId = wa.getFlowId();
        WorkflowMgr wfm = new WorkflowMgr();
        WorkflowDb wf = wfm.getWorkflowDb(flowId);

        WorkflowPredefineDb wfp = new WorkflowPredefineDb();
        wfp = wfp.getPredefineFlowOfFree(wf.getTypeCode());

        if (wa.getStatus()== WorkflowActionDb.STATE_DOING || wa.getStatus()== WorkflowActionDb.STATE_RETURN) {
            ;
        } else {
            // 有可能会是重激活的情况
            if (!wfp.isReactive()) {
                return new Result<>(false, i18nUtil.get("processStatus") + wa.getStatus() + ", "+i18nUtil.get("mayHaveBeenProcess"));
            }
        }

        long newMyActionId = -1;
        try {
            newMyActionId = wfm.resume(request, myActionId);
        }
        catch (ErrMsgException e) {
            return new Result<>(false, e.getMessage());
        }
        JSONObject json = new JSONObject();
        json.put("newMyActionId", newMyActionId);
        return new Result<>(json);
    }

    @ApiOperation(value = "删除流程", notes = "删除流程", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/del", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> del(@RequestParam(value="flowId", required = true) Integer flowId) {
        boolean re;
        try {
            WorkflowMgr wm = new WorkflowMgr();
            re = wm.del(request, flowId);
        } catch (ErrMsgException e) {
            return new Result<>(responseUtil.getResJson(false, e.getMessage()));
        }
        return new Result<>(responseUtil.getResJson(re));
    }

    @ApiOperation(value = "删除流程中的附件", notes = "删除流程中的附件", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "attId", value = "附件ID", required = true, dataType = "Integer")
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/delAtt", method={RequestMethod.GET,RequestMethod.POST}, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> delAtt(@RequestParam(value="flowId", required = true) Integer flowId, @RequestParam(value="attId", required = true) Integer attId) {
        WorkflowDb wf = new WorkflowDb();
        wf = wf.getWorkflowDb(flowId);
        Document doc = new Document();
        doc = doc.getDocument(wf.getDocId());
        DocContent dc = doc.getDocContent(1);
        Attachment att = new Attachment(attId);
        boolean re = dc.delAttachment(attId);
        if (re) {
            // 清空对应的field中的值
            String fieldName = att.getFieldName();
            if (!StringUtils.isEmpty(fieldName)) {
                if (flowId != -1) {
                    Leaf lf = new Leaf();
                    lf = lf.getLeaf(wf.getTypeCode());
                    FormDb fd = new FormDb();
                    fd = fd.getFormDb(lf.getFormCode());
                    FormField ff = fd.getFormField(fieldName);
                    if (ff != null) {
                        FormDAO fdao = new FormDAO();
                        fdao = fdao.getFormDAOByCache(flowId, fd);

                        if (ff.getMacroType().equals("macro_attachments")) {
                            String val = fdao.getFieldValue(fieldName);
                            StringBuilder sb = new StringBuilder();
                            String[] ary = val.split(";");
                            for (String s : ary) {
                                String[] ary2 = s.split(",");
                                if (ary2.length == 2) {
                                    if (ary2[0].equals(String.valueOf(flowId))) {
                                        if (ary2[1].equals(String.valueOf(attId))) {
                                            continue;
                                        }
                                    }
                                }
                                StrUtil.concat(sb, ";", s);
                            }
                            fdao.setFieldValue(fieldName, sb.toString());
                        } else {
                            fdao.setFieldValue(fieldName, "");
                        }
                        try {
                            fdao.save();
                            // 如果需要记录历史
                            if (fd.isLog()) {
                                com.redmoon.oa.flow.FormDAO.log(authUtil.getUserName(), FormDAOLog.LOG_TYPE_EDIT, fdao);
                            }
                        } catch (ErrMsgException e) {
                            LogUtil.getLog(getClass()).error(e);
                        }
                    }
                    else {
                        DebugUtil.e(getClass(), "delAttach", "字段：" + fieldName + "不存在");
                    }
                }
            }
        }
        return new Result<>(re);
    }

    @ApiOperation(value = "删除文件宏控件中的附件", notes = "删除文件宏控件中的附件", httpMethod = "POST")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/delAttach", method={RequestMethod.GET,RequestMethod.POST}, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> delAttach() {
        int flowId = ParamUtil.getInt(request, "flowId", -1);
        boolean re = false;
        int attachId = ParamUtil.getInt(request, "attachId", -1);
        boolean isFlow = ParamUtil.getBoolean(request, "isFlow", true);
        if (isFlow && flowId != -1) {
            WorkflowDb wf = new WorkflowDb();
            wf = wf.getWorkflowDb(flowId);
            if (!wf.isLoaded()) {
                return new Result<>(false, "流程: " + flowId + " 不存在");
            }

            int docId = wf.getDocId();
            Document doc = new Document();
            doc = doc.getDocument(docId);
            DocContent dc = doc.getDocContent(1);
            Attachment att = new Attachment(attachId);
            re = dc.delAttachment(attachId);
            if (re) {
                // 清空对应的field中的值
                String fieldName = att.getFieldName();
                if (!StringUtils.isEmpty(fieldName)) {
                    Leaf lf = new Leaf();
                    lf = lf.getLeaf(wf.getTypeCode());
                    FormDb fd = new FormDb();
                    fd = fd.getFormDb(lf.getFormCode());
                    if (fd.getFormField(fieldName) != null) {
                        FormDAO fdao = new FormDAO();
                        fdao = fdao.getFormDAOByCache(flowId, fd);
                        fdao.setFieldValue(fieldName, "");
                        try {
                            fdao.save();
                            // 如果需要记录历史
                            if (fd.isLog()) {
                                com.redmoon.oa.flow.FormDAO.log(authUtil.getUserName(), FormDAOLog.LOG_TYPE_EDIT, fdao);
                            }
                        } catch (ErrMsgException e) {
                            LogUtil.getLog(getClass()).error(e);
                        }
                    } else {
                        DebugUtil.e(getClass(), "delAttach", "字段：" + fieldName + "不存在");
                    }
                }
            }
        } else {
            if (attachId != -1) {
                com.redmoon.oa.visual.Attachment att = new com.redmoon.oa.visual.Attachment(attachId);
                re = att.del();
            } else {
                String diskName = ParamUtil.get(request, "diskName");
                if (!StrUtil.isEmpty(diskName)) {
                    com.redmoon.oa.visual.Attachment att = new com.redmoon.oa.visual.Attachment();
                    att = att.getAttachment(diskName);
                    re = att.del();
                }
                else {
                    return new Result<>(false, "diskName is empty.");
                }
            }
        }
        return new Result<>(re);
    }

    @ApiOperation(value = "删除文件组宏控件中的附件", notes = "删除文件组宏控件中的附件", httpMethod = "POST")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/delAttachForAttsCtl", method={RequestMethod.GET,RequestMethod.POST}, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> delAttachForAttsCtl() {
        String diskName = ParamUtil.get(request, "diskName");
        long attachId = ParamUtil.getLong(request, "attachId", -1);
        boolean re = false;

        if (!StrUtil.isEmpty(diskName)) {
            com.redmoon.oa.visual.Attachment att = new com.redmoon.oa.visual.Attachment();
            att = att.getAttachment(diskName);

            // 检查是否为映射的值
            boolean isMapped = true;
            long visualId = att.getVisualId();
            String fieldName = att.getFieldName();
            String formCode = att.getFormCode();
            FormDb fd = new FormDb();
            fd = fd.getFormDb(formCode);
            com.redmoon.oa.visual.FormDAO dao = new com.redmoon.oa.visual.FormDAO();
            dao = dao.getFormDAOByCache(visualId, fd);
            Vector<IAttachment> vAtt = dao.getAttachments();
            for (IAttachment iatt : vAtt) {
                // 如果能在dao记录的附件中找到
                if (iatt.getId() == attachId) {
                    isMapped = false;
                    break;
                }
            }

            if (isMapped) {
                String val = dao.getFieldValue(fieldName);
                String[] ary = val.split(";");
                StringBuilder sb = new StringBuilder();
                for (String s : ary) {
                    if (!s.equals(String.valueOf(attachId))) {
                        StrUtil.concat(sb, ";", s);
                    }
                }
                dao.setFieldValue(fieldName, sb.toString());
                re = dao.save();
            } else {
                re = att.del();
            }
            return new Result<>(re);
        }

        if (attachId == -1) {
            return new Result<>(false, "标识非法");
        }

        int flowId = ParamUtil.getInt(request, "flowId", -1);
        String pageType = ParamUtil.get(request, "pageType");
        if (flowId == -1) {
            // 嵌套表格中删除
            com.redmoon.oa.visual.Attachment att = new com.redmoon.oa.visual.Attachment(attachId);
            if (att.isLoaded()) {
                // 检查是否为映射的值，如果是，则不能删除附件，只能清掉fieldName中映射的值
                boolean isMapped = ParamUtil.getBoolean(request, "isMapped", false);
                if (isMapped) {
                    long visualId = ParamUtil.getLong(request, "visualId", -1);
                    String formCode = ParamUtil.get(request, "formCode");
                    String fieldName = ParamUtil.get(request, "fieldName");
                    FormDb fd = new FormDb();
                    fd = fd.getFormDb(formCode);
                    com.redmoon.oa.visual.FormDAO dao = new com.redmoon.oa.visual.FormDAO();
                    dao = dao.getFormDAOByCache(visualId, fd);

                    String val = dao.getFieldValue(fieldName);
                    String[] ary = val.split(";");
                    StringBuilder sb = new StringBuilder();
                    for (String s : ary) {
                        if (!s.equals(String.valueOf(attachId))) {
                            StrUtil.concat(sb, ";", s);
                        }
                    }
                    dao.setFieldValue(fieldName, sb.toString());
                    re = dao.save();
                } else {
                    re = att.del();
                }
                return new Result<>(re);
            }
        }

        if (flowId != -1 && !ConstUtil.PAGE_TYPE_EDIT_RELATE.equals(pageType)) {
            Attachment att = new Attachment(attachId);
            if (att.isLoaded()) {
                WorkflowDb wf = new WorkflowDb();
                wf = wf.getWorkflowDb(flowId);
                int docId = wf.getDocId();
                // int docId = ParamUtil.getInt(request, "docId", -1);
                Document doc = new Document();
                doc = doc.getDocument(docId);
                DocContent dc = doc.getDocContent(1);
                re = dc.delAttachment((int)attachId);
                if (re) {
                    // 清空对应的field中的值
                    String fieldName = att.getFieldName();
                    if (!StringUtils.isEmpty(fieldName)) {
                        Leaf lf = new Leaf();
                        lf = lf.getLeaf(wf.getTypeCode());
                        FormDb fd = new FormDb();
                        fd = fd.getFormDb(lf.getFormCode());
                        if (fd.getFormField(fieldName) != null) {
                            FormDAO fdao = new FormDAO();
                            fdao = fdao.getFormDAOByCache(flowId, fd);

                            String val = fdao.getFieldValue(fieldName);
                            StringBuilder sb = new StringBuilder();
                            String[] ary = val.split(";");
                            for (String s : ary) {
                                String[] ary2 = s.split(",");
                                if (ary2.length == 2) {
                                    if (ary2[0].equals(String.valueOf(flowId))) {
                                        if (ary2[1].equals(String.valueOf(attachId))) {
                                            continue;
                                        }
                                    }
                                }
                                StrUtil.concat(sb, ";", s);
                            }
                            fdao.setFieldValue(fieldName, sb.toString());
                            try {
                                fdao.save();
                                // 如果需要记录历史
                                if (fd.isLog()) {
                                    com.redmoon.oa.flow.FormDAO.log(authUtil.getUserName(), FormDAOLog.LOG_TYPE_EDIT, fdao);
                                }
                            } catch (ErrMsgException e) {
                                LogUtil.getLog(getClass()).error(e);
                            }
                        } else {
                            DebugUtil.e(getClass(), "delAttach", "字段：" + fieldName + "不存在");
                        }
                    }
                }
            }
        }

        return new Result<>(re);
    }

    @ApiOperation(value = "选择退回节点", notes = "选择退回节点", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "actionId", value = "流程节点ID", required = true, dataType = "Integer"),
    })
    @ResponseBody
    @RequestMapping(value = "/flow/returnAction", method={RequestMethod.GET,RequestMethod.POST}, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public String returnAction(@RequestParam(value = "flowId", required = false)Integer flowId, @RequestParam(value = "actionId")Integer actionId) {
        com.alibaba.fastjson.JSONArray ary = new com.alibaba.fastjson.JSONArray();
        String sql = "select id from flow_my_action where flow_id=" + flowId + " and is_checked<>" + MyActionDb.CHECK_STATUS_NOT + " and is_checked<>" + MyActionDb.CHECK_STATUS_WAITING_TO_DO + " and is_checked<>" + MyActionDb.CHECK_STATUS_WAITING_TO_DO_IN_ACTION + " order by receive_date desc";
        MyActionDb mad = new MyActionDb();
        Vector v = mad.list(sql);
        Map<String, String> map = new HashMap<>();
        WorkflowActionDb wa = new WorkflowActionDb();

        wa = wa.getWorkflowActionDb(actionId);
        Vector<WorkflowActionDb> vcfrom = wa.getLinkFromActions();
        String userName = authUtil.getUserName();

        // 如果之前曾经处理过本节点，则说明有可能当前是被退回
        // 取得用户最早处理本节点的待办记录ID
        long firstMyActionId = -1;
        Iterator ir = v.iterator();
        while (ir.hasNext()) {
            mad = (MyActionDb) ir.next();
            if (mad.getActionId() == actionId && mad.getChecker().equals(userName)) {
                firstMyActionId = mad.getId();
            }
        }

        String checked = "";
        if (v.size()==1) {
            checked = "checked";
        }
        ir = v.iterator();
        while (ir.hasNext()) {
            mad = (MyActionDb)ir.next();

            // 如果存在最早处理记录，则退回时，mad需小于最早记录的ID，即只能退回在最早记录之前处理的用户
            if (firstMyActionId != -1) {
                if (mad.getId() >= firstMyActionId) {
                    continue;
                }
            }
            // 如果记录对应本节点，则跳过
            if (mad.getActionId() == actionId) {
                continue;
            }

            long aId = mad.getActionId();
            wa = wa.getWorkflowActionDb((int)aId);

            // 是否为兄弟节点
            boolean isSibling = false;
            Vector<WorkflowActionDb> vtfrom = wa.getLinkFromActions();
            for (WorkflowActionDb workflowActionDb : vcfrom) {
                for (WorkflowActionDb actionDb : vtfrom) {
                    if (workflowActionDb.getId() == actionDb.getId()) {
                        isSibling = true;
                        break;
                    }
                }
            }

            if (isSibling) {
                // 如果是兄弟节点，但还需再判断一下从该节点至当前节点有没有连线，如果有，则应允许返回至该兄弟节点
                // 比如从节点2与节点3是兄弟节点，但节点2至节点3有连线，则节点3返回时，应可返回至节点2
                // ----[ 节点1  ]-|-------------------|---[ 节点3  ]-----
                //                |-----[ 节点2  ]----|
                boolean hasLinkFromSibling = false;
                for (WorkflowActionDb workflowActionDb : vcfrom) {
                    if (workflowActionDb.getId() == wa.getId()) {
                        hasLinkFromSibling = true;
                        break;
                    }
                }
                if (!hasLinkFromSibling) {
                    continue;
                }
            }

            if (map.get(String.valueOf(aId))!=null) {
                continue;
            }

            map.put(String.valueOf(aId), String.valueOf(aId));

            com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
            ary.add(json);
            json.put("returnId", wa.getId());
            json.put("checked", checked);
            json.put("actionTitle", wa.getTitle());
            json.put("userRealName", wa.getUserRealName());
            json.put("checkDate", DateUtil.format(mad.getCheckDate(), "yyyy-MM-dd HH:mm"));
        }

        com.alibaba.fastjson.JSONObject jsonObject = responseUtil.getResJson(true);
        jsonObject.put("result", ary);
        return jsonObject.toString();
    }

    @ApiOperation(value = "放弃流程", notes = "放弃流程", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/flow/discard", method={RequestMethod.GET,RequestMethod.POST}, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> discard(@RequestParam(value="flowId", required = true)Integer flowId) {
        String myname = authUtil.getUserName();
        WorkflowMgr wfm = new WorkflowMgr();
        boolean re;
        try {
            re = wfm.discard(request, myname, flowId);
        } catch (ErrMsgException e) {
            return new Result<>(responseUtil.getResJson(false, e.getMessage()));
        }

        return new Result<>(responseUtil.getResJson(re));
    }

    @ApiOperation(value = "下载文件", notes = "下载文件", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "attName", value = "附件名称", required = false, dataType = "String"),
            @ApiImplicitParam(name = "visualPath", value = "附件路径", required = true, dataType = "String"),
            @ApiImplicitParam(name = "diskName", value = "附件磁盘名称", required = true, dataType = "String"),
    })
    @RequestMapping(value="/flow/downloadFile", method={RequestMethod.GET,RequestMethod.POST})
    public void downloadFile(HttpServletResponse response, @RequestParam(value="flowId", required = true)Integer flowId,
                             @RequestParam(value="attachId", required = true)Integer attachId,
                             @RequestParam(value = "attName", required = false)String attName,
                             @RequestParam(value = "visualPath")String visualPath,
                             @RequestParam(value = "diskName")String diskName) throws IOException, ErrMsgException {
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        String userName = privilege.getUser(request);
        // 判断是否超出下载次数限制
        AttachmentLogMgr alm = new AttachmentLogMgr();
        if (!alm.canDownload(userName, flowId, attachId)) {
            throw new ErrMsgException(alm.getErrMsg(request));
        }

        // 下载记录存至日志
        AttachmentLogMgr.log(userName, flowId, attachId, AttachmentLogDb.TYPE_DOWNLOAD);

        fileService.preview(response, visualPath + "/" + diskName);
    }

    @ApiOperation(value = "下载文件", notes = "下载文件", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "attachId", value = "附件ID", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "visitKey", value = "访问token", required = false, dataType = "String"),
    })
    @RequestMapping(value="/flow/download", method={RequestMethod.GET,RequestMethod.POST})
    public void download(HttpServletResponse response, @RequestParam(value="flowId")Integer flowId, @RequestParam(value="attachId")Integer attachId, @RequestParam(value = "visitKey", required = false)String visitKey) throws IOException, ErrMsgException {
        boolean isFlow = ParamUtil.getBoolean(request, "isFlow", true);
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        String userName = privilege.getUser(request);
        // 判断是否超出下载次数限制
        AttachmentLogMgr alm = new AttachmentLogMgr();
        if (!alm.canDownload(userName, flowId, attachId)) {
            try (BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream())) {
                response.setContentType("text/html;charset=utf-8");
                String str = alm.getErrMsg(request);
                bos.write(str.getBytes(StandardCharsets.UTF_8));
            }
            catch (final IOException e) {
                LogUtil.getLog(getClass()).error(e);
            }
            return;
        }

        if (isFlow) {
            Attachment att = new Attachment(attachId);
            if (!att.isLoaded()) {
                throw new ErrMsgException("文件不存在，flowId=" + flowId + " id=" + attachId);
            }

            /*String fileName = att.getName();
            String fileDiskPath = cn.js.fan.web.Global.getRealPath() + att.getVisualPath() + "/" + att.getDiskName();
            String op = ParamUtil.get(request, "op");
            if ("toPDF".equals(op)) {
                String fName = fileName.substring(0, fileName.lastIndexOf("."));
                fileName = fName + ".pdf";

                String diskName = att.getDiskName();
                String pdfName = diskName.substring(0, diskName.lastIndexOf("."));
                pdfName += ".pdf";

                String pdfPath = cn.js.fan.web.Global.getRealPath() + att.getVisualPath() + "/" + pdfName;
                try {
                    com.redmoon.oa.util.PDFConverter.convert2pdf(fileDiskPath, pdfPath);
                }
                catch (Exception e) {
                    throw new ErrMsgException("转换PDF失败：" + e.getMessage());
                }
                fileDiskPath = pdfPath;
            }*/

            // 下载记录存至日志
            AttachmentLogMgr.log(userName, flowId, attachId, AttachmentLogDb.TYPE_DOWNLOAD);

            fileService.download(response, att.getName(), att.getVisualPath(), att.getDiskName());
        } else {
            com.redmoon.oa.visual.Attachment att = new com.redmoon.oa.visual.Attachment();
            att = att.getAttachment(attachId);
            if (!att.isLoaded()) {
                throw new ErrMsgException("模块文件不存在，flowId=" + flowId + " id=" + attachId);
            }
            // 下载记录存至日志
            AttachmentLogMgr.log(userName, flowId, attachId, AttachmentLogDb.TYPE_DOWNLOAD);

            fileService.download(response, att.getName(), att.getVisualPath(), att.getDiskName());
        }
    }

    @ApiOperation(value = "下载OFD文件", notes = "下载OFD文件", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "attachId", value = "附件ID", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "visitKey", value = "访问token", required = false, dataType = "String"),
    })
    @RequestMapping(value="/flow/downloadOfd", method={RequestMethod.GET,RequestMethod.POST})
    public void downloadOfd(HttpServletResponse response, @RequestParam(value="flowId")Integer flowId, @RequestParam(value="attachId")Integer attachId, @RequestParam(value = "visitKey", required = false)String visitKey) throws IOException, ErrMsgException {
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        String userName = privilege.getUser(request);
        // 判断是否超出下载次数限制
        AttachmentLogMgr alm = new AttachmentLogMgr();
        if (!alm.canDownload(userName, flowId, attachId)) {
            try (BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream())) {
                response.setContentType("text/html;charset=utf-8");
                String str = alm.getErrMsg(request);
                bos.write(str.getBytes(StandardCharsets.UTF_8));
            }
            catch (final IOException e) {
                LogUtil.getLog(getClass()).error(e);
            }
            return;
        }
        Attachment att = new Attachment(attachId);
        if (!att.isLoaded()) {
            throw new ErrMsgException("文件不存在，flowId=" + flowId + " id=" + attachId);
        }
        workflowService.convertToOfd(flowId,att,userName,"","");//docx 转 ofd
        String fileName = att.getName();
        String fName = fileName.substring(0, fileName.lastIndexOf("."));//取文件名
        fileName = fName + ".ofd";
        String diskName = att.getDiskName();
        String ofdDiskName = diskName.substring(0, diskName.lastIndexOf(".")) + ".ofd";//取diskName
        // 下载记录存至日志
        AttachmentLogMgr.log(userName, flowId, attachId, AttachmentLogDb.TYPE_DOWNLOAD);
        fileService.download(response, fileName, att.getVisualPath(), ofdDiskName);
    }

    @ApiOperation(value = "获取公文模板文件", notes = "获取公文模板文件", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "ID", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "type", value = "ID", required = false, dataType = "String"),
    })
    @RequestMapping(value="/flow/getTemplateFile", method={RequestMethod.GET,RequestMethod.POST})
    public void getTemplateFile(HttpServletResponse response, @RequestParam(value="id")Integer id, @RequestParam(value = "type", required = false)String type) throws IOException, ErrMsgException {
        DocTemplateMgr dtm = new DocTemplateMgr();
        DocTemplateDb dtd = dtm.getDocTemplateDb(id);
        if (!dtd.isLoaded()) {
            try (BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream())) {
                response.setContentType("text/html;charset=utf-8");
                String str = "模板：" + id + " 文件不存在";
                bos.write(str.getBytes(StandardCharsets.UTF_8));
            }
            catch (final IOException e) {
                LogUtil.getLog(getClass()).error(e);
            }
            return;
        }
        if ("tail".equals(type)) {
            fileService.download(response, dtd.getFileName(), "upfile/" + DocTemplateDb.linkBasePath + "/" + dtd.getFileNameTail());
        } else {
            fileService.download(response, dtd.getFileName(), "upfile/" + DocTemplateDb.linkBasePath + "/" + dtd.getFileName());
        }
    }

    @ApiOperation(value = "下载回复中的附件", notes = "下载回复中的附件", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "附件id", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @RequestMapping(value="/flow/downloadAnnexAttachment", method={RequestMethod.GET,RequestMethod.POST})
    public void downloadAnnexAttachment(HttpServletResponse response, @RequestParam(value = "id") Integer id) throws IOException, ErrMsgException {
        WorkflowAnnexAttachment workflowAnnexAttachment = new WorkflowAnnexAttachment(id);
        if (!workflowAnnexAttachment.isLoaded()) {
            try (BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream())) {
                response.setContentType("text/html;charset=utf-8");
                String str = "文件：" + id + " 文件不存在";
                bos.write(str.getBytes(StandardCharsets.UTF_8));
            }
            catch (final IOException e) {
                LogUtil.getLog(getClass()).error(e);
            }
            return;
        }
        fileService.download(response, workflowAnnexAttachment.getName(), workflowAnnexAttachment.getVisualPath(), workflowAnnexAttachment.getDiskName());
    }

    @ApiOperation(value = "流程处理初始化", notes = "流程处理初始化", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "myActionId", value = "待办记录的ID", required = true, dataType = "Long"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @RequestMapping(value = "/flow/flowProcess", method={RequestMethod.GET,RequestMethod.POST})
    @ResponseBody
    public Result<Object> flowProcess(@RequestParam(value="myActionId")Long myActionId) {
        long t = System.currentTimeMillis();
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        String myname = privilege.getUser(request);

        MyActionDb mad = new MyActionDb();
        mad = mad.getMyActionDb(myActionId);
        if (!mad.isLoaded()) {
            return new Result<>(false, i18nUtil.get("myActionNotExist"));
        }

        int flowId = (int) mad.getFlowId();
        WorkflowMgr wfm = new WorkflowMgr();
        WorkflowDb wf = wfm.getWorkflowDb(flowId);

        com.alibaba.fastjson.JSONObject jsonResult = new com.alibaba.fastjson.JSONObject();
        // 如果存在子流程，则处理子流程
        if (mad.getSubMyActionId() != MyActionDb.SUB_MYACTION_ID_NONE) {
            // return "forward:flowDispose.do?myActionId=" + mad.getSubMyActionId();
            jsonResult.put("res", 2);
            jsonResult.put("toDo", "process");
            jsonResult.put("myActionId", mad.getSubMyActionId());
            return new Result<>(jsonResult);
        }

        UserMgr um = new UserMgr();
        UserDb myUser = um.getUserDb(myname);

        String myRealName = myUser.getRealName();

        WorkflowPredefineDb wpd = new WorkflowPredefineDb();
        wpd = wpd.getDefaultPredefineFlow(wf.getTypeCode());

        boolean isFlowManager = false;
        if (privilege.isUserPrivValid(request, "admin")) {
            isFlowManager = true;
        } else {
            LeafPriv lp = new LeafPriv(wf.getTypeCode());
            if (privilege.isUserPrivValid(request, "admin.flow")) {
                if (lp.canUserExamine(privilege.getUser(request))) {
                    isFlowManager = true;
                }
            }
        }

        boolean isRecall = wpd.isRecall(); // 是否能撤回

        WorkflowActionDb wa = new WorkflowActionDb();
        int actionId = (int) mad.getActionId();
        wa = wa.getWorkflowActionDb(actionId);

        if (Global.getInstance().isDebug()) {
            DebugUtil.i(getClass(), "flowProcess getWorkflowActionDb", String.valueOf(((double)System.currentTimeMillis() - t)/1000));
        }
        // 判断能否提交
        try {
            WorkflowMgr.canSubmit(request, wf, wa, mad, myname, wpd);
        } catch (ErrMsgException e) {
            // return new Result<>(false, e.getMessage());
            jsonResult.put("res", 1);
            jsonResult.put("flowId", mad.getFlowId());
            jsonResult.put("cwsWorkflowTitle", wf.getTitle());
            jsonResult.put("msg", e.getMessage());
            return new Result<>(jsonResult);
        }
        if (Global.getInstance().isDebug()) {
            DebugUtil.i(getClass(), "flowProcess canSubmit", String.valueOf(((double)System.currentTimeMillis() - t)/1000));
        }

        // 锁定流程
        if (wa.getKind() != WorkflowActionDb.KIND_READ) {
            wfm.lock(wf, myname);
        }

        // 如果是未读状态
        if (!mad.isReaded()) {
            mad.setReaded(true);
            mad.setReadDate(new java.util.Date());
            mad.save();
        }

        String flag = wa.getFlag();

        Leaf lf = new Leaf();
        lf = lf.getLeaf(wf.getTypeCode());

        // 置嵌套表需要用到的cwsId
        request.setAttribute("cwsId", "" + flowId);
        // 置嵌套表需要用到的pageType
        request.setAttribute("pageType", ConstUtil.PAGE_TYPE_FLOW);
        // 置NestFromCtl及NestSheetCtl需要用到的workflowActionId
        request.setAttribute("workflowActionId", "" + wa.getId());
        // 置macro_js_ntko.jsp中需要用到的myActionId
        request.setAttribute("myActionId", "" + myActionId);

        com.redmoon.oa.Config cfg = com.redmoon.oa.Config.getInstance();
        boolean canUserSeeDesignerWhenDispose = cfg.getBooleanProperty("canUserSeeDesignerWhenDispose");
        // boolean canUserModifyFlow = cfg.getBooleanProperty("canUserModifyFlow");
        boolean canUserSeeFlowChart = cfg.getBooleanProperty("canUserSeeFlowChart");

        String flowExpireUnit = cfg.get("flowExpireUnit");
        boolean isHour = !"day".equals(flowExpireUnit);
        if ("day".equals(flowExpireUnit)) {
            flowExpireUnit = i18nUtil.get("day");
        } else {
            flowExpireUnit = i18nUtil.get("hour");
        }

        String action = ParamUtil.get(request, "action");

        WorkflowRuler wr = new WorkflowRuler();
        com.redmoon.oa.flow.FlowConfig conf = new com.redmoon.oa.flow.FlowConfig();             //用于判断流程toolbar按钮是否显示

        // 取得节点上对应的dao（节点上可能设置了表单）
        FormDAO fdao = workflowService.getFormDAOByAction(wf, wa);
        FormDb fd = fdao.getFormDb();

        // 置NestSheetCtl需要用到的formCode
        request.setAttribute("formCode", fd.getCode());

        boolean hasApproverSelectByStarter = workflowService.hasApproverSelectByStarter(wf);
        jsonResult.put("hasApproverSelectByStarter", hasApproverSelectByStarter);

        // 用于当表单域选择宏控件中含有条件{$mainId}时，通过ModuleUtil.getModuleListSelCondScriptFromWinOpener获取
        jsonResult.put("cws_id", fdao.getId());

        String exts = cfg.get("flowFileExt");
        String[] extAry = StrUtil.split(exts, ",");
        // 设置可上传的文件类型，用于 obs上传
        jsonResult.put("validExt", extAry);
        // 设置最大可上传的文件大小，以K为单位
        jsonResult.put("maxFileSize", Global.FileSize);
        jsonResult.put("visualPath", fdao.getVisualPath());
        // docx文件能否转成ofd格式下载
        jsonResult.put("isWordDownloadByOfd", cfg.getInt("isWordDownloadByOfd"));
        // ofd格式文件是否可以预览
        jsonResult.put("canOfdFilePreview", cfg.getBooleanProperty("canOfdFilePreview"));
        // 运行节点预处理事件，注意保存草稿后，每次再进入时会再次运行
        WorkflowPredefineMgr wpm = new WorkflowPredefineMgr();
        String script = wpm.getActionPreDisposeScript(wpd.getScripts(), wa.getInternalName());
        if (script != null) {
            try {
                wfm.runPreDisposeScript(request, privilege.getUser(request), wf, fdao, mad, script);
            } catch (ErrMsgException e) {
                // LogUtil.getLog(getClass()).error(e);
                return new Result<>(false, "预处理事件：" + e.getMessage());
            }
        }

        if (Global.getInstance().isDebug()) {
            DebugUtil.i(getClass(), "flowProcess runPreDisposeScript", String.valueOf(((double)System.currentTimeMillis() - t)/1000));
        }

        String userName1 = privilege.getUser(request);
        UserSetupDb userSetupDb = new UserSetupDb();
        userSetupDb = userSetupDb.getUserSetupDb(userName1);
        if (!userSetupDb.isLoaded()) {
            throw new ErrMsgException("用户: " + userName1 + " 在配置表(user_setup)中的记录不存在");
        }
        String str = userSetupDb.getLocal();
        boolean isEn = "en-US".equals(str);
        boolean isReturnStyleFree = wpd.getReturnStyle()==WorkflowPredefineDb.RETURN_STYLE_FREE;
        boolean isAutoSaveArchive = flag.length()>=5 && "2".equals(flag.substring(4, 5));
        boolean isStartNode = wa.getIsStart()==0;

        // 是否为流程管理员
        jsonResult.put("isFlowManager", isFlowManager);
        jsonResult.put("formCode", lf.getFormCode());
        jsonResult.put("formName", fd.getName());
        jsonResult.put("flowId", flowId);
        jsonResult.put("myActionId", myActionId);
        jsonResult.put("myUserName", myname);
        jsonResult.put("myRealName", myRealName);
        jsonResult.put("random", Math.random());
        jsonResult.put("isEn", isEn);
        jsonResult.put("skey", new Privilege().getSkey(myname));

        jsonResult.put("officeEditor", sysProperties.isWebOfficeEnabled() ? "web-office" : sysProperties.isOnlyOfficeEnabled() ? "onlyoffice" : "");
        if (sysProperties.isOnlyOfficeEnabled()) {
            jsonResult.put("onlyofficeServerUrl", sysProperties.getOnlyOfficeServerUrl());
        }
        jsonResult.put("canEditAttachment", (sysProperties.isWebOfficeEnabled() || sysProperties.isOnlyOfficeEnabled()) && wa.canEditAttachment());
        int branchMode = StrUtil.toInt(WorkflowActionDb.getActionProperty(wpd, wa.getInternalName(), "branchMode"), WorkflowActionDb.BRANCH_MODE_MULTI);
        jsonResult.put("branchMode", branchMode);

        ModuleSetupDb msd = new ModuleSetupDb();
        msd = msd.getModuleSetupDb(lf.getFormCode());
        String formJs = ModuleUtil.parseScript(flowId, fdao.getId(), lf.getFormCode(), null, -1, null, null, ConstUtil.PAGE_TYPE_LIST, msd.getScript("form_js"));
        jsonResult.put("formJs", StrUtil.getNullStr(formJs));

        if (Global.getInstance().isDebug()) {
            DebugUtil.i(getClass(), "flowProcess parseScript", String.valueOf(((double)System.currentTimeMillis() - t)/1000));
        }

        jsonResult.put("activeTabTitle", wf.getTitle().replaceAll("\r\n", "").trim().length() >= 8 ? wf.getTitle().replaceAll("\r\n", "").trim().substring(0, 8) : wf.getTitle().replaceAll("\r\n", "").trim());
        jsonResult.put("action", action);
        jsonResult.put("isAutoSaveArchive", isAutoSaveArchive);
        jsonResult.put("isFlowReturnWithRemark", cfg.getBooleanProperty("isFlowReturnWithRemark"));
        jsonResult.put("isReturnStyleFree", isReturnStyleFree);
        jsonResult.put("actionId", actionId);
        jsonResult.put("isStartNode", isStartNode);
        jsonResult.put("canUserSeeDesignerWhenDispose", canUserSeeDesignerWhenDispose);
        jsonResult.put("isRecall", isRecall);
        jsonResult.put("isRemarkShowInProcessList", cfg.getBoolean("isRemarkShowInProcessList"));

        FormViewDb formViewDb = new FormViewDb();
        Vector vView = formViewDb.getViews(lf.getFormCode());
        boolean hasView = false;
        if (vView.size()>0) {
            hasView = true;
            com.alibaba.fastjson.JSONArray aryView = new com.alibaba.fastjson.JSONArray();
            Iterator irView = vView.iterator();
            while (irView.hasNext()) {
                formViewDb = (FormViewDb)irView.next();
                com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
                json.put("id", formViewDb.getLong("id"));
                json.put("name", formViewDb.getString("name"));
                aryView.add(json);
            }
            jsonResult.put("aryView", aryView);
        }
        jsonResult.put("hasView", hasView);

        if (Global.getInstance().isDebug()) {
            DebugUtil.i(getClass(), "flowProcess hasView", String.valueOf(((double)System.currentTimeMillis() - t)/1000));
        }

        int doc_id = wf.getDocId();
        DocumentMgr dm = new DocumentMgr();
        Document doc = dm.getDocument(doc_id);
        Render rd = new Render(request, wf, doc);
        String content = "";
        try {
            content = rd.rend(wa);
        } catch (ErrMsgException e) {
            LogUtil.getLog(getClass()).error(e);
        }

        if (Global.getInstance().isDebug()) {
            DebugUtil.i(getClass(), "flowProcess rend", String.valueOf(((double)System.currentTimeMillis() - t)/1000));
        }

        jsonResult.put("isRendWithoutViewRule", fd.isRendWithoutViewRule());
        // 如果不分离渲染
        if (!fd.isRendWithoutViewRule()) {
            content += flowRender.rendScriptByAction(actionId, false);
        }

        String strIsShowNextUsers = WorkflowActionDb.getActionProperty(wpd, wa.getInternalName(), "isShowNextUsers");
        boolean isNotShowNextUsers = "0".equals(strIsShowNextUsers);

        String strIsBtnSaveShow = WorkflowActionDb.getActionProperty(wpd, wa.getInternalName(), "isBtnSaveShow");
        boolean isBtnSaveShow = !"0".equals(strIsBtnSaveShow);

        jsonResult.put("isNotShowNextUsers", isNotShowNextUsers);

        boolean isPlus = mad.getActionStatus() == WorkflowActionDb.STATE_PLUS;
        int plusType;
        boolean isPlusBefore = false;
        String plusDesc = "";
        boolean isMyPlus = false;
        if (!"".equals(wa.getPlus())) {
            com.alibaba.fastjson.JSONObject plusJson = com.alibaba.fastjson.JSONObject.parseObject(wa.getPlus());
            plusType = plusJson.getIntValue("type");
            isPlusBefore = plusType == WorkflowActionDb.PLUS_TYPE_BEFORE;
            String from = plusJson.getString("from");
            if (myname.equals(from)) {
                isMyPlus = true;
                plusDesc = workflowService.getPlusDesc(plusJson);
            }
        }

        jsonResult.put("isPlus", isPlus);
        jsonResult.put("isPlusBefore", isPlusBefore);
        jsonResult.put("isMyPlus", isMyPlus);
        jsonResult.put("plusDesc", plusDesc);

        boolean isMyActionExpire = mad.getExpireDate() != null;
        jsonResult.put("isMyActionExpire", isMyActionExpire);
        jsonResult.put("expirationDate", mad.getExpireDate());
        jsonResult.put("isActionSendMsg", wa.isMsg());
        jsonResult.put("canDelAttachment", wa.canDelAttachment());
        jsonResult.put("isUseSms", com.redmoon.oa.sms.SMSFactory.isUseSMS());
        jsonResult.put("flowAutoSMSRemind", cfg.getBooleanProperty("flowAutoSMSRemind"));
        jsonResult.put("flowAutoMsgRemind", cfg.getBooleanProperty("flowAutoMsgRemind"));

        jsonResult.put("isFlowLevelDisplay", cfg.getBooleanProperty("isFlowLevelDisplay"));
        jsonResult.put("isFlowStarted", wf.isStarted());
        // jsonResult.put("levelImg", WorkflowMgr.getLevelImg(request, wf));

        String flowTitle;
        if (wf.getTitle().startsWith("#")) {
            flowTitle = LocalUtil.LoadString(request, "res.ui.menu", wf.getTypeCode());
        } else {
            flowTitle = wf.getTitle();
        }
        boolean isFlowTitleReadonly = !StrUtil.isEmpty(lf.getDescription());
        jsonResult.put("flowTitle", flowTitle);
        jsonResult.put("isFlowTitleReadonly", isFlowTitleReadonly);

        String starterName = wf.getUserName();
        String starterRealName = "";
        if (starterName != null) {
            UserDb starter = um.getUserDb(wf.getUserName());
            starterRealName = starter.getRealName();
        }
        jsonResult.put("starterRealName", starterRealName);
        jsonResult.put("beginDate", wf.getBeginDate());
        jsonResult.put("isFlowFinished", wf.getStatus() == WorkflowDb.STATUS_FINISHED);

        jsonResult.put("isReactive", wpd.isReactive());
        jsonResult.put("isAlter", wf.isAlter());
        if (wf.isAlter()) {
            UserCache userCache = SpringUtil.getBean(UserCache.class);
            String alterUserRealName = "";
            User alterUser = userCache.getUser(wf.getAlterUser());
            if (alterUser != null) {
                alterUserRealName = alterUser.getRealName();
            }
            jsonResult.put("alterUserRealName", alterUserRealName);
            jsonResult.put("alterTime", wf.getAlterTime());
        }

        jsonResult.put("flowIsRemarkShow", cfg.getBooleanProperty("flowIsRemarkShow"));
        jsonResult.put("flowRemarkPos", cfg.getInt("flowRemarkPos"));
        // jsonResult.put("isOpinionRelateWithRemark", cfg.getBooleanProperty("isOpinionRelateWithRemark"));
        jsonResult.put("myActionResult", mad.getResult());

        jsonResult.put("flowPerformanceDisplay", cfg.getBooleanProperty("flowPerformanceDisplay"));
        jsonResult.put("flowExpireUnit", flowExpireUnit);

        jsonResult.put("aryMyAction", workflowService.listProcess(flowId));

        com.alibaba.fastjson.JSONArray aryReturnAction = new com.alibaba.fastjson.JSONArray();
        boolean hasReturnAction = false;
        Vector<WorkflowActionDb> returnv = wa.getLinkReturnActions();
        if (returnv.size() > 0 || wpd.getReturnStyle() == WorkflowPredefineDb.RETURN_STYLE_FREE) {
            hasReturnAction = true;
            for (WorkflowActionDb returnwa : returnv) {
                if (returnwa.getStatus() != WorkflowActionDb.STATE_IGNORED) {
                    com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject();
                    jsonObject.put("actionId", returnwa.getId());
                    jsonObject.put("actionTitle", returnwa.getTitle());
                    jsonObject.put("realName", returnwa.getUserRealName());
                    aryReturnAction.add(jsonObject);
                }
            }
        }
        jsonResult.put("hasReturnAction", hasReturnAction);
        jsonResult.put("aryReturnAction", aryReturnAction);

        if (Global.getInstance().isDebug()) {
            DebugUtil.i(getClass(), "flowProcess aryReturnAction", String.valueOf(((double)System.currentTimeMillis() - t)/1000));
        }

        jsonResult.put("isHasAttachment", fd.isHasAttachment());
        int aryAttSize = 0;
        if (doc!=null) {
            aryAttSize = doc.getAttachments(1).size();
        }
        jsonResult.put("aryAttSize", aryAttSize);

        if (lf.getQueryId() != Leaf.QUERY_NONE) {
            // 判断权限，管理员能看见查询，其它人员根据角色进行判断
            String[] roles = StrUtil.split(lf.getQueryRole(), ",");
            boolean canSeeQuery = false;
            if (!privilege.isUserPrivValid(request, "admin")) {
                if (roles != null) {
                    UserDb user = new UserDb();
                    user = user.getUserDb(privilege.getUser(request));
                    for (String role : roles) {
                        if (user.isUserOfRole(role)) {
                            canSeeQuery = true;
                            break;
                        }
                    }
                } else {
                    canSeeQuery = true;
                }
            } else {
                canSeeQuery = true;
            }
            FormQueryDb aqd = new FormQueryDb();
            aqd = aqd.getFormQueryDb((int) lf.getQueryId());
            boolean canQuery = canSeeQuery && aqd.isLoaded();
            jsonResult.put("canQuery", canQuery);
            if (canQuery) {
                jsonResult.put("queryId", lf.getQueryId());
                jsonResult.put("queryName", aqd.getQueryName());
                String colratio = "";
                String colP = aqd.getColProps();
                if (colP == null || "".equals(colP)) {
                    colP = "[]";
                }
                int tableWidth = 0;
                com.alibaba.fastjson.JSONArray jsonArray = com.alibaba.fastjson.JSONArray.parseArray(colP);
                for (int i = 0; i < jsonArray.size(); i++) {
                    com.alibaba.fastjson.JSONObject jsonCol = jsonArray.getJSONObject(i);
                    if (jsonCol.getBoolean("hide")) {
                        continue;
                    }
                    String name = (String) jsonCol.get("name");
                    if ("cws_op".equalsIgnoreCase(name)) {
                        continue;
                    }
                    tableWidth += jsonCol.getIntValue("width");
                    if ("".equals(colratio)) {
                        colratio = jsonCol.getString("width");
                    } else {
                        colratio += "," + jsonCol.getString("width");
                    }
                }
                jsonResult.put("colRatio", colratio);
                jsonResult.put("queryTableWidth", tableWidth + 2);
                String queryAjaxUrl;
                if (aqd.isScript()) {
                    queryAjaxUrl = "/flow/formQueryListScriptEmbed";
                } else {
                    queryAjaxUrl = "/flow/formQueryListEmbed";
                }
                jsonResult.put("queryAjaxUrl", queryAjaxUrl);
                com.alibaba.fastjson.JSONObject queryCond = com.alibaba.fastjson.JSONObject.parseObject(lf.getQueryCondMap());
                jsonResult.put("queryCond", queryCond);
            }
        }

        MacroCtlMgr mm = new MacroCtlMgr();
        // 判断是否需按选项卡的方式显示
        /*StringBuilder sbUl = new StringBuilder();
        StringBuilder sbDiv = new StringBuilder();
        FormDb formDbOther = new FormDb();
        for (FormField macroField : fd.getFields()) {
            if (macroField.getType().equals(FormField.TYPE_MACRO)) {
                MacroCtlUnit mu = mm.getMacroCtlUnit(macroField.getMacroType());
                if (mu == null) {
                    DebugUtil.e(getClass(), "flowProcess", macroField.getTitle() + " 宏控件: " + macroField.getMacroType() + " 不存在");
                } else if (mu.getNestType() == MacroCtlUnit.NEST_TYPE_NORMAIL) {
                    String destForm = macroField.getDescription();
                    String defaultVal = StrUtil.decodeJSON(destForm);
                    JSONObject jsonDef = JSONObject.parseObject(defaultVal);
                    int isTab = 0;
                    if (jsonDef.containsKey("isTab")) {
                        isTab = jsonDef.getIntValue("isTab");
                        if (isTab == 1) {
                            destForm = jsonDef.getString("destForm");
                            formDbOther = formDbOther.getFormDb(destForm);
                            sbUl.append("<li><a href='#tabs-" + destForm + "'>" + formDbOther.getName() + "</a></li>");
                            sbDiv.append("<div id='tabs-" + destForm + "' class='tabDiv'></div>");
                        }
                    }
                }
            }
        }

        boolean isTab = false;
        if (sbUl.length() > 0) {
            isTab = true;
        }
        jsonResult.put("isTab", isTab);
        String strUl = "<ul>" + "<li><a href='#tabs-" + fd.getCode() + "'>" + fd.getName() + "</a></li>" + sbUl.toString() + "</ul>";
        String strDiv = "<div id='tabs-" + fd.getCode() + "' class='tabDiv'>" + content + "</div>" + sbDiv.toString();
        jsonResult.put("tabs", strUl + strDiv);*/

        jsonResult.put("content", content);
        jsonResult.put("isReply", wpd.isReply());
        jsonResult.put("flowStatus", wf.getStatus());
        jsonResult.put("isProgress", fd.isProgress());

        if (fd.isProgress()) {
            jsonResult.put("progress", fdao.getCwsProgress());
        }

        jsonResult.put("isReturnBack", wf.isReturnBack());

        jsonResult.put("PLUS_TYPE_BEFORE", WorkflowActionDb.PLUS_TYPE_BEFORE);
        jsonResult.put("PLUS_TYPE_AFTER", WorkflowActionDb.PLUS_TYPE_AFTER);
        jsonResult.put("PLUS_TYPE_CONCURRENT", WorkflowActionDb.PLUS_TYPE_CONCURRENT);

        jsonResult.put("PLUS_MODE_ORDER", WorkflowActionDb.PLUS_MODE_ORDER);
        jsonResult.put("PLUS_MODE_ONE", WorkflowActionDb.PLUS_MODE_ONE);
        jsonResult.put("PLUS_MODE_ALL", WorkflowActionDb.PLUS_MODE_ALL);

        jsonResult.put("isDebug", lf.isDebug());
        // 如果是debug模式，则更新流程
        if (lf.isDebug()) {
            wf.refreshFlow();
        }

        String fieldWrite = "," + StrUtil.getNullString(wa.getFieldWrite()).trim() + ",";
        String fieldHide = "," + StrUtil.getNullString(wa.getFieldHide()).trim() + ",";
        Vector<FormField> vFields = fd.getFields();
        int formView = wa.getFormView();
        if (formView != WorkflowActionDb.VIEW_DEFAULT) {
            FormViewDb fvd = new FormViewDb();
            fvd = fvd.getFormViewDb(formView);
            String form = fvd.getString("content");
            String ieVersion = fvd.getString("ie_version");
            FormParser fp = new FormParser();
            vFields = fp.parseCtlFromView(form, ieVersion, fd);
        }

        com.alibaba.fastjson.JSONArray aryFieldWritable = new com.alibaba.fastjson.JSONArray();
        Iterator<FormField> irFields = vFields.iterator();
        while (irFields.hasNext()) {
            FormField ff = irFields.next();
            boolean isWrite = fieldWrite.contains("," + ff.getName() + ",");
            com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
            json.put("fieldTitle", ff.getTitle());
            json.put("isWrite", isWrite);
            json.put("fieldName", ff.getName());
            aryFieldWritable.add(json);
        }
        jsonResult.put("aryFieldWritable", aryFieldWritable);

        if (Global.getInstance().isDebug()) {
            DebugUtil.i(getClass(), "flowProcess aryFieldWritable", String.valueOf(((double)System.currentTimeMillis() - t)/1000));
        }

        String[] fds = StrUtil.getNullString(wa.getFieldWrite()).trim().split(",");
        int len = fds.length;

        com.alibaba.fastjson.JSONArray aryNestFieldWritable = new com.alibaba.fastjson.JSONArray();
        // 取出嵌套表
        irFields = vFields.iterator();
        while (irFields.hasNext()) {
            FormField ff = irFields.next();
            if (ff.getType().equals(FormField.TYPE_MACRO)) {
                MacroCtlUnit mu = mm.getMacroCtlUnit(ff.getMacroType());
                if (mu == null) {
                    DebugUtil.e(getClass(), "flowProcess", ff.getTitle() + " 宏控件: " + ff.getMacroType() + " 不存在");
                } else if (mu.getNestType() != MacroCtlUnit.NEST_TYPE_NONE) {
                    String defaultVal = "";
                    String nestFormCode = ff.getDefaultValue();
                    if (mu.getNestType() == MacroCtlUnit.NEST_DETAIL_LIST) {
                        defaultVal = StrUtil.decodeJSON(ff.getDescription());
                    } else {
                        defaultVal = ff.getDescription();
                        if ("".equals(defaultVal)) {
                            defaultVal = ff.getDefaultValueRaw();
                        }
                        defaultVal = StrUtil.decodeJSON(defaultVal); // ff.getDefaultValueRaw()
                    }
                    // 20131123 fgf 添加
                    JSONObject json = JSONObject.parseObject(defaultVal);
                    if (json == null) {
                        LogUtil.getLog(getClass()).warn("嵌套表: " + ff.getTitle() + " 属性非法");
                        continue;
                    }
                    nestFormCode = json.getString("destForm");
                    if (nestFormCode == null) {
                        LogUtil.getLog(getClass()).warn(ff.getTitle() + " 嵌套表不能为空");
                        continue;
                    }

                    msd = msd.getModuleSetupDbOrInit(nestFormCode);
                    FormDb nestfd = new FormDb();
                    nestfd = nestfd.getFormDb(msd.getFormCode());

                    String[] fields = msd.getColAry(false, "list_field");
                    for (FormField ff2 : nestfd.getFields()) {
                        String txt = ff.getTitle() + "=>" + ff2.getTitle(); // + "(" + nestfd.getName() + ")";
                        // 判断是否已被选中
                        boolean isFound = false;
                        for (int i = 0; i < len; i++) {
                            if (("nest." + ff2.getName()).equals(fds[i])) {
                                isFound = true;
                                break;
                            }
                        }
                        com.alibaba.fastjson.JSONObject jsonObj = new com.alibaba.fastjson.JSONObject();
                        jsonObj.put("fieldTitle", ff2.getTitle());
                        jsonObj.put("fieldName", ff2.getName());
                        jsonObj.put("fieldText", txt);
                        jsonObj.put("isWrite", isFound);
                        aryNestFieldWritable.add(jsonObj);
                    }
                }
            }
        }
        jsonResult.put("aryNestFieldWritable", aryNestFieldWritable);
        if (Global.getInstance().isDebug()) {
            DebugUtil.i(getClass(), "flowProcess aryNestFieldWritable", String.valueOf(((double)System.currentTimeMillis() - t)/1000));
        }

        com.alibaba.fastjson.JSONArray aryFieldHide = new com.alibaba.fastjson.JSONArray();
        irFields = vFields.iterator();
        while (irFields.hasNext()) {
            FormField ff = irFields.next();
            boolean isHide = fieldHide.contains("," + ff.getName() + ",");
            com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
            json.put("isHide", isHide);
            json.put("fieldTitle", ff.getTitle());
            json.put("fieldName", ff.getName());
            aryFieldHide.add(json);
        }
        jsonResult.put("aryFieldHide", aryFieldHide);

        if (lf.isDebug()) {
            // 连接器动作
            JSONArray aryConnAction = new JSONArray();
            String rules = wpd.getRules();
            if (!StrUtil.isEmpty(rules)) {
                JSONArray arr = JSONArray.parseArray(rules);
                for (Object o : arr) {
                    com.alibaba.fastjson.JSONObject json = (com.alibaba.fastjson.JSONObject) o;
                    if ("connector".equals(json.getString("ruleType")) &&
                            (wa.getInternalName().equals(json.getString("node")) || "request".equals(json.getString("nodeType")) || "finished".equals(json.getString("nodeType")))) {
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put("nodeOperate", json.getString("nodeOperate"));
                        jsonObject.put("title", json.getString("title"));
                        jsonObject.put("id", json.getString("id"));
                        aryConnAction.add(jsonObject);
                    }
                }
            }
            jsonResult.put("aryConnectorAction", aryConnAction);
        }

        com.alibaba.fastjson.JSONArray aryNestFieldHide = new com.alibaba.fastjson.JSONArray();
        fds = StrUtil.getNullString(wa.getFieldHide()).trim().split(",");
        len = fds.length;
        // 取出嵌套表
        irFields = vFields.iterator();
        while (irFields.hasNext()) {
            FormField ff = irFields.next();
            if (ff.getType().equals(FormField.TYPE_MACRO)) {
                MacroCtlUnit mu = mm.getMacroCtlUnit(ff.getMacroType());
                if (mu == null) {
                    DebugUtil.e(getClass(), "flowProcess", ff.getTitle() + " 宏控件: " + ff.getMacroType() + " 不存在");
                } else if (mu.getNestType() != MacroCtlUnit.NEST_TYPE_NONE) {
                    String nestFormCode = ff.getDefaultValue();
                    String defaultVal = "";
                    if (mu.getNestType() == MacroCtlUnit.NEST_DETAIL_LIST) {
                        defaultVal = StrUtil.decodeJSON(ff.getDescription());
                    } else {
                        defaultVal = ff.getDescription();
                        if ("".equals(defaultVal)) {
                            defaultVal = ff.getDefaultValueRaw();
                        }
                        defaultVal = StrUtil.decodeJSON(defaultVal); // ff.getDefaultValueRaw()
                    }

                    // 20131123 fgf 添加
                    JSONObject json = JSONObject.parseObject(defaultVal);
                    if (json == null) {
                        LogUtil.getLog(getClass()).warn("嵌套表: " + ff.getTitle() + " 未设置");
                        continue;
                    }
                    nestFormCode = json.getString("destForm");
                    if (nestFormCode == null) {
                        LogUtil.getLog(getClass()).warn(ff.getTitle() + " 嵌套表不能为空");
                        continue;
                    }

                    msd = msd.getModuleSetupDbOrInit(nestFormCode);
                    FormDb nestfd = new FormDb();
                    nestfd = nestfd.getFormDb(msd.getFormCode());

                    for (FormField ff2 : nestfd.getFields()) {
                        String txt = ff.getTitle() + "=>" + ff2.getTitle(); // + "(" + nestfd.getName() + ")";
                        // 判断是否已被选中
                        boolean isFound = false;
                        for (int i = 0; i < len; i++) {
                            if (("nest." + ff2.getName()).equals(fds[i])) {
                                isFound = true;
                                break;
                            }
                        }
                        com.alibaba.fastjson.JSONObject jsonObj = new com.alibaba.fastjson.JSONObject();
                        jsonObj.put("isHide", isFound);
                        jsonObj.put("fieldTitle", ff2.getTitle());
                        jsonObj.put("fieldName", ff2.getName());
                        jsonObj.put("fieldText", txt);
                        aryNestFieldHide.add(jsonObj);
                    }
                }
            }
        }
        jsonResult.put("aryNestFieldHide", aryNestFieldHide);
        if (Global.getInstance().isDebug()) {
            DebugUtil.i(getClass(), "flowProcess aryNestFieldHide", String.valueOf(((double)System.currentTimeMillis() - t)/1000));
        }

        jsonResult.put("unitCode", privilege.getUserUnitCode(request));
        jsonResult.put("tabIdOpener", ParamUtil.get(request, "tabIdOpener"));

        boolean isCustomRedirectUrl = false;
        String redirectUrl = WorkflowActionDb.getActionProperty(wpd, wa.getInternalName(), "redirectUrl");
        if (redirectUrl!=null && !"".equals(redirectUrl)) {
            isCustomRedirectUrl = true;
            if (!redirectUrl.contains("?")) {
                redirectUrl += "?";
            }
            else {
                redirectUrl += "&";
            }
        }
        jsonResult.put("isCustomRedirectUrl", isCustomRedirectUrl);
        jsonResult.put("redirectUrl", redirectUrl);

        jsonResult.put("isNotShowNextUsers", isNotShowNextUsers);

        boolean isActionKindRead = wa.getKind()==WorkflowActionDb.KIND_READ;
        jsonResult.put("isActionKindRead", isActionKindRead);

        boolean isDocDistributed = false;
        // 检查文件是否已分发
        if (!isActionKindRead && wf.isStarted() && wa.isDistribute()) {
            if (mad.getCheckStatus() != MyActionDb.CHECK_STATUS_SUSPEND) {
                PaperDistributeDb pdd = new PaperDistributeDb();
                int paperCount = pdd.getCountOfWorkflow(wf.getId());
                if (paperCount > 0) {
                    isDocDistributed = true;
                }
            }
        }
        jsonResult.put("isDocDistributed", isDocDistributed);

        Vector<FormField> fields = fd.getFields();
        fds = fieldWrite.split(",");
        len = fds.length;
        //将不可写的域筛选出
        for (FormField ff : fields) {
            boolean finded = false;
            for (int i = 0; i < len; i++) {
                if (ff.getName().equals(fds[i])) {
                    finded = true;
                    break;
                }
            }

            if (!finded) {
                ff.setEditable(false);
            }
        }
        String checkJs = FormUtil.doGetCheckJS(request, fields);
        String checkJsSub = checkJs.substring(9, checkJs.length() - 10);
        jsonResult.put("checkJsSub", checkJsSub);

        jsonResult.put("canUserStartFlow", wr.canUserStartFlow(request, wf));
        jsonResult.put("isBtnSaveShow", isBtnSaveShow && conf.getIsDisplay("FLOW_BUTTON_SAVE"));

        jsonResult.put("aryButton", workflowService.getToolbarButtons(wpd, lf, wf, mad, wa, returnv, flag, myname, isBtnSaveShow, isActionKindRead, canUserSeeDesignerWhenDispose, canUserSeeFlowChart));

        jsonResult.put("btnCancelAttention", conf.getBtnName("FLOW_BUTTON_CANCEL_ATTENTION").startsWith("#") ? i18nUtil.get("cancelAttention") : conf.getBtnName("FLOW_BUTTON_CANCEL_ATTENTION"));
        jsonResult.put("btnAttention", conf.getBtnName("FLOW_BUTTON_ATTENTION").startsWith("#")?i18nUtil.get("attention"):conf.getBtnName("FLOW_BUTTON_ATTENTION"));
        jsonResult.put("PLUS_TYPE_CONCURRENT", WorkflowActionDb.PLUS_TYPE_CONCURRENT);

        // 如果下一节点上设置为“表单中的人员”，或者限定了部门表单域，则绑定change事件，以重新匹配人员
        List<String> bindFieldList = new ArrayList<>();
        Vector<WorkflowActionDb> vto = wa.getLinkToActions();
        for (WorkflowActionDb towa : vto) {
            String jobCode = towa.getJobCode();
            if (jobCode.startsWith(WorkflowActionDb.PRE_TYPE_FIELD_USER)) {
                String fieldNames = jobCode.substring((WorkflowActionDb.PRE_TYPE_FIELD_USER + "_").length());
                if (!fieldNames.startsWith("nest.")) {
                    String[] fieldAry = StrUtil.split(fieldNames, ",");
                    if (fieldAry == null) {
                        continue;
                    }
                    Collections.addAll(bindFieldList, fieldAry);
                }
            } else {
                String deptField = WorkflowActionDb.getActionProperty(wpd, towa.getInternalName(), "deptField");
                if (!StringUtils.isEmpty(deptField)) {
                    bindFieldList.add(deptField);
                }

                // 限定部门表单域，只记录1个，即只支持单分支的情况
                jsonResult.put("deptFieldLimited", deptField);
                // 限定部门
                String deptLimited = towa.getDept().trim();
                jsonResult.put("deptLimited", deptLimited);
            }
        }
        jsonResult.put("bindFieldList", bindFieldList);

        if (Global.getInstance().isDebug()) {
            DebugUtil.i(getClass(), "flowProcess bindFieldList", String.valueOf(((double)System.currentTimeMillis() - t)/1000));
        }

        // 如果处理过程显示于下方
        boolean flowProcessShowOnBottom = cfg.getBooleanProperty("flowProcessShowOnBottom");
        jsonResult.put("flowProcessShowOnBottom", flowProcessShowOnBottom);

        com.alibaba.fastjson.JSONObject matchJson;
        try {
            matchJson = workflowService.matchBranchAndUser(request, myActionId, actionId);
        } catch (ErrMsgException e) {
            matchJson = new com.alibaba.fastjson.JSONObject();
            LogUtil.getLog(getClass()).error(e);
        }
        jsonResult.put("matchJson", matchJson);

        boolean isPageStyleLight = false;
        msd = msd.getModuleSetupDbOrInit(lf.getFormCode());
        int cfgPageStyle = cfg.getInt("pageStyle");
        if (cfgPageStyle == ConstUtil.PAGE_STYLE_LIGHT || (cfgPageStyle == ConstUtil.PAGE_STYLE_BY_FORM_SETUP && fd.getPageStyle() == ConstUtil.PAGE_STYLE_LIGHT)) {
            isPageStyleLight = true;
        }
        jsonResult.put("isPageStyleLight", isPageStyleLight);
        // jsonResult.put("skinPath", SkinMgr.getSkinPath(request, false));
        jsonResult.put("flowTypeCode", wpd.getTypeCode());
        jsonResult.put("level", wf.getLevel());
        jsonResult.put("levelDesc", WorkflowDb.getLevelDesc(wf.getLevel()));

        jsonResult.put("LEVEL_NORMAL", WorkflowDb.LEVEL_NORMAL);
        jsonResult.put("LEVEL_IMPORTANT", WorkflowDb.LEVEL_IMPORTANT);
        jsonResult.put("LEVEL_URGENT", WorkflowDb.LEVEL_URGENT);

        jsonResult.put("bindFieldList", bindFieldList);
        if (Global.getInstance().isDebug()) {
            DebugUtil.i(getClass(), "flowProcess end", String.valueOf(((double)System.currentTimeMillis() - t)/1000));
        }

        // 处理过程的表头
        WorkflowConfig workflowConfig = WorkflowConfig.getInstance();
        if (isFlowManager) {
            jsonResult.put("myActionListCols", workflowConfig.getProperty("flowMyActionListColsForManager"));
        } else {
            jsonResult.put("myActionListCols", workflowConfig.getProperty("flowMyActionListColsForNormal"));
        }
        return new Result<>(jsonResult);
    }

    @ApiOperation(value = "渲染显示规则脚本", notes = "流程处理初始化", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "myActionId", value = "待办记录的ID", required = true, dataType = "Long"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @RequestMapping(value = "/flow/flowProcessScript", method={RequestMethod.GET,RequestMethod.POST})
    @ResponseBody
    public Result<Object> flowProcessScript(@RequestParam(value="actionId")Integer actionId) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("script", flowRender.rendScriptByAction(actionId, false));
        return new Result<>(jsonObject);
    }

    @ApiOperation(value = "PC端处理自由流程", notes = "PC端处理自由流程", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "myActionId", value = "待办记录的ID", required = true, dataType = "Long"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/flow/flowProcessFree", method={RequestMethod.GET,RequestMethod.POST})
    public Result<Object> flowProcessFree(@RequestParam(value="myActionId")Long myActionId) {
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        String myname = privilege.getUser( request );

        UserMgr um = new UserMgr();
        UserDb myUser = um.getUserDb(myname);

        MyActionDb mad = new MyActionDb();
        mad = mad.getMyActionDb(myActionId);

        if (!mad.isLoaded()) {
            return new Result<>(false, "由于上一节点重新激活或撤回，待办记录已不存在");
        } else if (mad.getCheckStatus() == MyActionDb.CHECK_STATUS_TRANSFER) {
            return new Result<>(false, "流程已转办，不需要再处理");
        }

        // 权限检查
        if (!mad.getUserName().equals(myname) && !mad.getProxyUserName().equals(myname)) {
            return new Result<>(false, i18nUtil.get("pvg_invalid"));
        }

        WorkflowActionDb wa = new WorkflowActionDb();
        int actionId = (int)mad.getActionId();
        wa = wa.getWorkflowActionDb(actionId);
        if ( wa==null || !wa.isLoaded() ) {
            return new Result<>(false, "流程中的相应动作：" + actionId + "不存在！");
        }

        if (mad.getCheckStatus()==MyActionDb.CHECK_STATUS_TRANSFER) {
            return new Result<>(false, "流程已转办，不需要再处理");
        }

        int flowId = wa.getFlowId();

        // 置嵌套表需要用到的cwsId
        request.setAttribute("cwsId", "" + flowId);
        request.setAttribute("pageType", ConstUtil.PAGE_TYPE_FLOW);
        request.setAttribute("workflowActionId", "" + wa.getId());
        // 置macro_js_ntko.jsp中需要用到的myActionId
        request.setAttribute("myActionId", "" + myActionId);

        WorkflowMgr wfm = new WorkflowMgr();
        WorkflowDb wf = wfm.getWorkflowDb(flowId);
        if (!wf.isLoaded()) {
            return new Result<>(false, "流程不存在");
        }

        if (wf.getStatus()==WorkflowDb.STATUS_DELETED) {
            return new Result<>(false, i18nUtil.get("flowDeleted"));
        }

        Leaf lf = new Leaf();
        lf = lf.getLeaf(wf.getTypeCode());
        if (lf == null) {
            return new Result<>(false, "流程类型已被删除");
        }

        FormDb fd = new FormDb();
        fd = fd.getFormDb(lf.getFormCode());

        // 置NestSheetCtl需要用到的formCode
        request.setAttribute("formCode", lf.getFormCode());

        // 锁定流程
        wfm.lock(wf, myname);

        // 如果是未读状态
        if (!mad.isReaded()) {
            mad.setReaded(true);
            mad.setReadDate(new java.util.Date());
            mad.save();
        }

        WorkflowPredefineDb wfp = new WorkflowPredefineDb();
        wfp = wfp.getPredefineFlowOfFree(wf.getTypeCode());

        if (!wfp.isReactive()) {
            if (wa.getStatus()!= WorkflowActionDb.STATE_DOING && wa.getStatus()!= WorkflowActionDb.STATE_RETURN) {
                return new Result<>(false, "流程中动作节点不处在正处理或被打回状态，可能已经被处理过了");
            }
        }

        boolean isFlowManager = false;
        if (privilege.isUserPrivValid(request, "admin")) {
            isFlowManager = true;
        } else {
            if (wfp.canUserDo(myUser, wf.getTypeCode(), "del")) {
                isFlowManager = true;
            }
        }

        com.alibaba.fastjson.JSONObject jsonResult = new com.alibaba.fastjson.JSONObject();
        jsonResult.put("isFlowManager", isFlowManager);

        if (wfp.canUserDo(myUser, wf.getTypeCode(), "delAttach")) {
            jsonResult.put("canDelAttachment", true);
        } else {
            jsonResult.put("canDelAttachment", false);
        }

        String op = ParamUtil.get(request, "op");

        String action = ParamUtil.get(request, "action");

        FormDAO fdao = new FormDAO();
        fdao = fdao.getFormDAOByCache(flowId, fd);

        com.redmoon.oa.Config cfg = com.redmoon.oa.Config.getInstance();
        String flowExpireUnit = cfg.get("flowExpireUnit");
        boolean isHour = !"day".equals(flowExpireUnit);
        if ("day".equals(flowExpireUnit)) {
            flowExpireUnit = "天";
        } else {
            flowExpireUnit = "小时";
        }
        jsonResult.put("flowExpireUnit", flowExpireUnit);

        String exts = cfg.get("flowFileExt");
        String[] extAry = StrUtil.split(exts, ",");
        // 设置可上传的文件类型，用于 obs上传
        jsonResult.put("validExt", extAry);
        // 设置最大可上传的文件大小，以K为单位
        jsonResult.put("maxFileSize", Global.FileSize);
        jsonResult.put("visualPath", fdao.getVisualPath());

        jsonResult.put("action", action);
        jsonResult.put("formCode", lf.getFormCode());
        jsonResult.put("isReply", wfp.isReply());
        jsonResult.put("officeEditor", sysProperties.isWebOfficeEnabled() ? "web-office" : sysProperties.isOnlyOfficeEnabled() ? "onlyoffice" : "");
        if (sysProperties.isOnlyOfficeEnabled()) {
            jsonResult.put("onlyofficeServerUrl", sysProperties.getOnlyOfficeServerUrl());
        }
        jsonResult.put("canEditAttachment", sysProperties.isWebOfficeEnabled() || sysProperties.isOnlyOfficeEnabled());

        boolean isActionStateNotPlus = mad.getActionStatus() != WorkflowActionDb.STATE_PLUS;
        jsonResult.put("isActionStateNotPlus", isActionStateNotPlus);

        String formJs = "";
        ModuleSetupDb msd = new ModuleSetupDb();
        msd = msd.getModuleSetupDb(lf.getFormCode());
        if (!msd.getCode().equals(msd.getFormCode())) {
            ModuleSetupDb mainMsd = msd.getModuleSetupDb(msd.getFormCode());
            formJs = mainMsd.getScript("form_js");
        } else {
            formJs = msd.getScript("form_js");
        }
        formJs = ModuleUtil.parseScript(flowId, fdao.getId(), lf.getFormCode(), null, -1, null, null, ConstUtil.PAGE_TYPE_LIST, formJs);
        jsonResult.put("formJs", StrUtil.getNullStr(formJs));

        boolean isRoleMemberOfFlow = false;
        String rolesOfFlow = "";
        String[][] rolePrivs = wfp.getRolePrivsOfFree();
        int privLen = rolePrivs.length;
        for (int i = 0; i < privLen; i++) {
            if (rolePrivs[i][0].equals(RoleDb.CODE_MEMBER)) {
                isRoleMemberOfFlow = true;
            }
            if ("".equals(rolesOfFlow)) {
                rolesOfFlow = rolePrivs[i][0];
            } else {
                rolesOfFlow += "," + rolePrivs[i][0];
            }
        }
        jsonResult.put("rolesOfFlow", rolesOfFlow);

        String tabTitle = wf.getTitle().replaceAll("\r\n", "").trim().length() >= 8 ? wf.getTitle().replaceAll("\r\n", "").trim().substring(0, 8) : wf.getTitle().replaceAll("\r\n", "").trim();
        jsonResult.put("tabTitle", tabTitle);

        boolean isMyActionExpire = mad.getExpireDate() != null;
        jsonResult.put("isMyActionExpire", isMyActionExpire);
        jsonResult.put("expirationDate", mad.getExpireDate());

        jsonResult.put("isUseSMS", cfg.getBooleanProperty("isUseSMS"));
        jsonResult.put("flowAutoSMSRemind", cfg.getBooleanProperty("flowAutoSMSRemind"));
        jsonResult.put("flowAutoMsgRemind", cfg.getBooleanProperty("flowAutoMsgRemind"));

        com.alibaba.fastjson.JSONArray aryNextUser = new com.alibaba.fastjson.JSONArray();
        Vector<WorkflowActionDb> vto = wa.getLinkToActions();
        Iterator<WorkflowActionDb> irto = vto.iterator();
        String userRealNames="", nextActionUsers="";
        int count = 0;
        while (irto.hasNext()) {
            WorkflowActionDb toAction = irto.next();
            if ("".equals(nextActionUsers)) {
                nextActionUsers = toAction.getUserName();
                userRealNames = um.getUserDb(toAction.getUserName()).getRealName();
            } else {
                nextActionUsers += "," + toAction.getUserName();
                userRealNames += "," + um.getUserDb(toAction.getUserName()).getRealName();
            }
            count++;
            WorkflowLinkDb wld = new WorkflowLinkDb();
            wld = wld.getWorkflowLinkDbForward(wa, toAction);
            com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
            json.put("userNames", toAction.getUserName());

            String realNames = "";
            String userNames = toAction.getUserName().replaceAll("，", ",");
            String[] userAry = StrUtil.split(userNames, ",");
            if (userAry!=null) {
                for (String s : userAry) {
                    if ("".equals(realNames)) {
                        realNames = um.getUserDb(s).getRealName();
                    } else {
                        realNames += "，" + um.getUserDb(s).getRealName();
                    }
                }
            }
            json.put("realNames", realNames);
            json.put("expireHourPrefix", StrUtil.escape(toAction.getUserName()).toUpperCase());
            json.put("expireHour", wld.getExpireHour());
            aryNextUser.add(json);
        }
        jsonResult.put("aryNextUser", aryNextUser);
        jsonResult.put("userRealNames", userRealNames);
        jsonResult.put("nextActionUsers", nextActionUsers);

        wf = wfm.getWorkflowDb(flowId);
        Vector<WorkflowActionDb> returnv = wa.getLinkReturnActions();
        com.alibaba.fastjson.JSONArray aryReturnAction = new com.alibaba.fastjson.JSONArray();
        boolean hasReturnAction = false;
        if (returnv.size() > 0) {
            hasReturnAction = true;
            for (WorkflowActionDb returnwa : returnv) {
                if (returnwa.getStatus() != WorkflowActionDb.STATE_IGNORED) {
                    com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject();
                    jsonObject.put("actionId", returnwa.getId());
                    jsonObject.put("actionTitle", returnwa.getTitle());
                    jsonObject.put("realName", returnwa.getUserRealName());
                    aryReturnAction.add(jsonObject);
                }
            }
        }
        jsonResult.put("hasReturnAction", hasReturnAction);
        jsonResult.put("aryReturnAction", aryReturnAction);

        jsonResult.put("isFlowLevelDisplay", cfg.getBooleanProperty("isFlowLevelDisplay"));
        jsonResult.put("isFlowStarted", wf.isStarted());
        // jsonResult.put("levelImg", WorkflowMgr.getLevelImg(request, wf));
        jsonResult.put("level", wf.getLevel());
        jsonResult.put("LEVEL_NORMAL", WorkflowDb.LEVEL_NORMAL);
        jsonResult.put("LEVEL_IMPORTANT", WorkflowDb.LEVEL_IMPORTANT);
        jsonResult.put("LEVEL_URGENT", WorkflowDb.LEVEL_URGENT);

        jsonResult.put("flowTitle", wf.getTitle());
        jsonResult.put("flowTitleDefault", lf.getName());

        String starterName = wf.getUserName();
        String starterRealName = "";
        if (starterName != null) {
            UserDb starter = um.getUserDb(wf.getUserName());
            starterRealName = starter.getRealName();
        }
        jsonResult.put("starterRealName", starterRealName);
        jsonResult.put("beginDate", wf.getBeginDate());
        jsonResult.put("flowIsRemarkShow", cfg.getBooleanProperty("flowIsRemarkShow"));
        // jsonResult.put("isOpinionRelateWithRemark", cfg.getBooleanProperty("isOpinionRelateWithRemark"));
        jsonResult.put("myActionResult", mad.getResult());

        jsonResult.put("aryMyAction", workflowService.listProcess(flowId));

        int doc_id = wf.getDocId();
        DocumentMgr dm = new DocumentMgr();
        Document doc = dm.getDocument(doc_id);
        UserDb user = new UserDb();
        user = user.getUserDb(privilege.getUser(request));

        jsonResult.put("isHasAttachment", fd.isHasAttachment());

        jsonResult.put("flowId", flowId);
        jsonResult.put("actionId", actionId);
        jsonResult.put("myActionId", myActionId);

        if (lf.getQueryId() != Leaf.QUERY_NONE) {
            // 判断权限，管理员能看见查询，其它人员根据角色进行判断
            String[] roles = StrUtil.split(lf.getQueryRole(), ",");
            boolean canSeeQuery = false;
            if (!privilege.isUserPrivValid(request, "admin")) {
                if (roles != null) {
                    for (String role : roles) {
                        if (user.isUserOfRole(role)) {
                            canSeeQuery = true;
                            break;
                        }
                    }
                } else {
                    canSeeQuery = true;
                }
            } else {
                canSeeQuery = true;
            }
            FormQueryDb aqd = new FormQueryDb();
            aqd = aqd.getFormQueryDb((int) lf.getQueryId());
            boolean canQuery = canSeeQuery && aqd.isLoaded();
            jsonResult.put("canQuery", canQuery);
            if (canQuery) {
                jsonResult.put("queryId", lf.getQueryId());
                jsonResult.put("queryName", aqd.getQueryName());
                String colratio = "";
                String colP = aqd.getColProps();
                if (colP == null || "".equals(colP)) {
                    colP = "[]";
                }
                int tableWidth = 0;
                com.alibaba.fastjson.JSONArray jsonArray = com.alibaba.fastjson.JSONArray.parseArray(colP);
                for (int i = 0; i < jsonArray.size(); i++) {
                    com.alibaba.fastjson.JSONObject jsonCol = jsonArray.getJSONObject(i);
                    if (jsonCol.getBoolean("hide")) {
                        continue;
                    }
                    String name = (String) jsonCol.get("name");
                    if ("cws_op".equalsIgnoreCase(name)) {
                        continue;
                    }
                    tableWidth += jsonCol.getIntValue("width");
                    if ("".equals(colratio)) {
                        colratio = jsonCol.getString("width");
                    } else {
                        colratio += "," + jsonCol.getString("width");
                    }
                }
                jsonResult.put("colRatio", colratio);
                jsonResult.put("queryTableWidth", tableWidth + 2);
                String queryAjaxUrl;
                if (aqd.isScript()) {
                    queryAjaxUrl = "/flow/formQueryListScriptEmbed";
                } else {
                    queryAjaxUrl = "/flow/formQueryListEmbed";
                }
                jsonResult.put("queryAjaxUrl", queryAjaxUrl);
                com.alibaba.fastjson.JSONObject queryCond = com.alibaba.fastjson.JSONObject.parseObject(lf.getQueryCondMap());
                jsonResult.put("queryCond", queryCond);
            }
        }
        Render rd = new Render(request, wf, doc);
        String content = rd.rendFree(wa);
        jsonResult.put("isRendWithoutViewRule", fd.isRendWithoutViewRule());
        // 如果不分离渲染
        if (!fd.isRendWithoutViewRule()) {
            content += flowRender.rendScriptByAction(actionId, false);
        }
        jsonResult.put("content", content);

        jsonResult.put("isFlowStarted", wf.isStarted());
        jsonResult.put("flowStatus", wf.getStatus());
        jsonResult.put("isProgress", fd.isProgress());

        jsonResult.put("myUserName", user.getName());
        jsonResult.put("myRealName", user.getRealName());

        com.alibaba.fastjson.JSONArray aryAnnex = new com.alibaba.fastjson.JSONArray();
        WorkflowAnnexDb wad = new WorkflowAnnexDb();
        Vector<WorkflowAnnexDb> vec1 = wad.listRoot(flowId, myname);
        for (WorkflowAnnexDb workflowAnnexDb : vec1) {
            wad = workflowAnnexDb;

            com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
            json.put("realName", um.getUserDb(wad.getString("user_name")).getRealName());
            json.put("process", wad.getInt("progress"));
            json.put("content", wad.getString("content"));
            json.put("id", wad.getLong("id"));
            json.put("addDate", wad.getDate("add_date"));
            json.put("flowId", wad.getString("flow_id"));
            json.put("actionId", wad.getString("action_id"));
            json.put("userName", wad.getString("user_name"));
            aryAnnex.add(json);

            WorkflowAnnexDb wad2 = new WorkflowAnnexDb();
            com.alibaba.fastjson.JSONArray aryAnnexSub = new com.alibaba.fastjson.JSONArray();
            Vector<WorkflowAnnexDb> vec2 = wad2.listChildren(wad.getInt("id"), myname);
            for (WorkflowAnnexDb annexDb : vec2) {
                wad2 = annexDb;
                int id2 = (int) wad2.getLong("id");

                com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject();
                jsonObject.put("id", id2);
                jsonObject.put("userName", wad2.getString("user_name"));
                jsonObject.put("realName", um.getUserDb(wad2.getString("user_name")).getRealName());
                jsonObject.put("content", wad2.getString("content"));
                jsonObject.put("addDate", wad2.getDate("add_date"));
                jsonObject.put("flowId", wad2.getString("flow_id"));
                jsonObject.put("actionId", wad2.getString("action_id"));
                jsonObject.put("replyRealName", um.getUserDb(wad2.getString("reply_name")).getRealName());
                aryAnnexSub.add(jsonObject);
            }
            json.put("aryAnnexSub", aryAnnexSub);
        }
        jsonResult.put("aryAnnex", aryAnnex);
        jsonResult.put("isReturnBack", wf.isReturnBack());

        jsonResult.put("PLUS_TYPE_BEFORE", WorkflowActionDb.PLUS_TYPE_BEFORE);
        jsonResult.put("PLUS_TYPE_AFTER", WorkflowActionDb.PLUS_TYPE_AFTER);
        jsonResult.put("PLUS_TYPE_CONCURRENT", WorkflowActionDb.PLUS_TYPE_CONCURRENT);

        jsonResult.put("PLUS_MODE_ORDER", WorkflowActionDb.PLUS_MODE_ORDER);
        jsonResult.put("PLUS_MODE_ONE", WorkflowActionDb.PLUS_MODE_ONE);
        jsonResult.put("PLUS_MODE_ALL", WorkflowActionDb.PLUS_MODE_ALL);

        jsonResult.put("unitCode", privilege.getUserUnitCode(request));

        jsonResult.put("aryButton", workflowService.getToolbarBottonsFree(wfp, lf, wf, mad, wa, returnv, myname));

        com.redmoon.oa.flow.FlowConfig conf = new com.redmoon.oa.flow.FlowConfig();
        String btnNameCancelAttention = conf.getBtnName("FLOW_BUTTON_CANCEL_ATTENTION").startsWith("#")?i18nUtil.get("cancelAttention"):conf.getBtnName("FLOW_BUTTON_CANCEL_ATTENTION");
        String btnTitleCancelAttention = conf.getBtnTitle("FLOW_BUTTON_CANCEL_ATTENTION").startsWith("#")?i18nUtil.get("cancelAttention"):conf.getBtnTitle("FLOW_BUTTON_CANCEL_ATTENTION");
        jsonResult.put("btnNameCancelAttention", btnNameCancelAttention);
        jsonResult.put("btnTitleCancelAttention", btnTitleCancelAttention);

        String btnNameAttention = conf.getBtnName("FLOW_BUTTON_ATTENTION").startsWith("#")?LocalUtil.LoadString(request,"res.flow.Flow","attention"):conf.getBtnName("FLOW_BUTTON_ATTENTION");
        String btnTitleAttention = conf.getBtnTitle("FLOW_BUTTON_ATTENTION").startsWith("#")?LocalUtil.LoadString(request,"res.flow.Flow","attention"):conf.getBtnTitle("FLOW_BUTTON_ATTENTION");
        jsonResult.put("btnNameAttention", btnNameAttention);
        jsonResult.put("btnTitleAttention", btnTitleAttention);

        boolean isPageStyleLight = false;
        int cfgPageStyle = cfg.getInt("pageStyle");
        if (cfgPageStyle == ConstUtil.PAGE_STYLE_LIGHT || (cfgPageStyle == ConstUtil.PAGE_STYLE_BY_FORM_SETUP && fd.getPageStyle() == ConstUtil.PAGE_STYLE_LIGHT)) {
            isPageStyleLight = true;
        }
        jsonResult.put("isPageStyleLight", isPageStyleLight);
        jsonResult.put("isRemarkShowInProcessList", cfg.getBoolean("isRemarkShowInProcessList"));

        // 处理过程的表头
        WorkflowConfig workflowConfig = WorkflowConfig.getInstance();
        if (isFlowManager) {
            jsonResult.put("myActionListCols", workflowConfig.getProperty("flowMyActionListColsForManager"));
        } else {
            jsonResult.put("myActionListCols", workflowConfig.getProperty("flowMyActionListColsForNormal"));
        }
        return new Result<>(jsonResult);
    }

    @ApiOperation(value = "重置列属性", notes = "重置列属性", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "typeCode", value = "流程类型编码", required = true, dataType = "String"),
            @ApiImplicitParam(name = "displayMode", value = "显示模式（查询、待办、我的流程、关注的流程）", required = true, dataType = "Integer"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @PreAuthorize("hasAnyAuthority('admin')")
    @RequestMapping(value = "/flow/resetColProps", method={RequestMethod.GET,RequestMethod.POST}, produces={"text/html;charset=UTF-8;","application/json;charset=UTF-8;"})
    @ResponseBody
    public Result<Object> resetColProps(@RequestParam(value = "typeCode", required = false)String typeCode, @RequestParam(value = "displayMode", required = false) Integer displayMode) {
        Leaf colLeaf = new Leaf();
        FormDb fd = new FormDb();
        if (!"".equals(typeCode)) {
            colLeaf = colLeaf.getLeaf(typeCode);
        }
        if ("".equals(typeCode)) {
            colLeaf = colLeaf.getLeaf(Leaf.CODE_ROOT);
            colLeaf.setColProps("");
        }
        else {
            colLeaf.setColProps(com.redmoon.oa.flow.Leaf.getDefaultColProps(request, typeCode, displayMode).toString());
        }
        return new Result<>(colLeaf.update());
    }

    @ApiOperation(value = "取得列的设置", notes = "取得列的设置", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "code", value = "编码", dataType = "String"),
            @ApiImplicitParam(name = "displayMode", value = "显示模式（查询、待办、我的流程、关注的流程）", dataType = "Integer"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    // @PreAuthorize("hasAnyAuthority('admin')")
    @RequestMapping(value = "/flow/getColProps", method={RequestMethod.POST}, produces={"text/html;charset=UTF-8;","application/json;charset=UTF-8;"})
    @ResponseBody
    public Result<Object> getColProps(@RequestParam(value = "typeCode", required = false)String typeCode, @RequestParam(value = "displayMode", required = false) Integer displayMode) {
        Leaf colLeaf = new Leaf();
        JSONArray colProps = new JSONArray();
        if (!StrUtil.isEmpty(typeCode)) {
            colLeaf = colLeaf.getLeaf(typeCode);
        }
        else {
            colLeaf = colLeaf.getLeaf(Leaf.CODE_ROOT);
        }

        if (colLeaf.isLoaded() && !StrUtil.isEmpty(colLeaf.getColProps())) {
            colProps = com.alibaba.fastjson.JSONArray.parseArray(colLeaf.getColProps());
        }
        if (colProps == null) {
            colProps = com.redmoon.oa.flow.Leaf.getDefaultColProps(request, typeCode, displayMode);
        }
        return new Result<>(colProps);
    }

    @ApiOperation(value = "取表单中的字段", notes = "取表单中的字段", httpMethod = "POST")
    @ApiImplicitParam(name = "typeCode", value = "流程类型", required = false, dataType = "String")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/getFlowListFields", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> getFlowListFields(@RequestParam(value = "typeCode", required = false) String typeCode) {
        Leaf colLeaf = new Leaf();
        JSONArray colProps = new JSONArray();
        if (!StrUtil.isEmpty(typeCode)) {
            colLeaf = colLeaf.getLeaf(typeCode);
        } else {
            colLeaf = colLeaf.getLeaf(Leaf.CODE_ROOT);
        }

        int mode = ParamUtil.getInt(request, "displayMode", WorkflowMgr.DISPLAY_MODE_SEARCH);
        if (colLeaf.isLoaded() && !StrUtil.isEmpty(colLeaf.getColProps())) {
            colProps = com.alibaba.fastjson.JSONArray.parseArray(colLeaf.getColProps());
        }
        if (colProps == null) {
            colProps = com.redmoon.oa.flow.Leaf.getDefaultColProps(request, typeCode, mode);
        } else {
            // 按顺序拼接f.id、f.flow_level、f.flow_title、f.type_code...及不在colProps中的字段
            JSONArray defaultColProps = com.redmoon.oa.flow.Leaf.getDefaultColProps(request, typeCode, mode);
            JSONArray arrNot = new JSONArray();
            for (Object defo : defaultColProps) {
                JSONObject defJson = (JSONObject)defo;
                String field = defJson.getString("field");
                boolean isFound = false;
                for (Object o : colProps) {
                    JSONObject json = (JSONObject) o;
                    if (field.equals(json.getString("field")) || field.equals(json.getString("name"))) {
                        isFound = true;
                        break;
                    }
                }
                if (!isFound) {
                    arrNot.add(defJson);
                }
            }

            if (colLeaf.getType() != Leaf.TYPE_NONE) {
                FormDb fd = new FormDb();
                fd = fd.getFormDb(colLeaf.getFormCode());
                Vector<FormField> fields = fd.getFields();
                for (FormField ff : fields) {
                    boolean isFound = false;
                    for (Object o : colProps) {
                        JSONObject json = (JSONObject) o;
                        if (ff.getName().equals(json.getString("field")) || ff.getName().equals(json.getString("name"))) {
                            isFound = true;
                            break;
                        }
                    }
                    if (!isFound) {
                        JSONObject json = new JSONObject();
                        json.put("field", ff.getName());
                        json.put("title", ff.getTitle());
                        arrNot.add(json);
                    }
                }
            }
            colProps.addAll(arrNot);
        }
        return new Result<>(colProps);
    }

    @ApiOperation(value = "获取有权限发起的流程目录树", notes = "获取有权限发起的流程目录树，只能选择layer=3的节点", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "parentCode", value = "父节点编码", required = false, dataType = "String"),
            @ApiImplicitParam(name = "isIcon", value = "是否带有图标", required = false, dataType = "Boolean"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/getDirTree", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<com.alibaba.fastjson.JSONArray> getDirTree(@RequestParam(value="parentCode", required = false) String parentCode, @RequestParam(value="isIcon", defaultValue = "true")Boolean isIcon) {
        if (StrUtil.isEmpty(parentCode)) {
            parentCode = Leaf.CODE_ROOT;
        }
        Leaf lf = new Leaf();
        lf = lf.getLeaf(parentCode);
        DirectoryView dv = new DirectoryView(lf);
        com.alibaba.fastjson.JSONObject jsonRoot = new com.alibaba.fastjson.JSONObject();
        // 如果是根节点，则将code置为空，以免在前台看到是“全部类别”却查不出来引起误解
        jsonRoot.put("code", lf.getCode());
        jsonRoot.put("parentCode", lf.getParentCode());
        jsonRoot.put("name", lf.getName());
        jsonRoot.put("layer", lf.getLayer());
        jsonRoot.put("type", lf.getType());
        dv.getTree(lf, jsonRoot, isIcon);

        com.alibaba.fastjson.JSONArray ary = new com.alibaba.fastjson.JSONArray();
        ary.add(jsonRoot);
        return new Result<>(ary);
    }

    @ApiOperation(value = "获取调度需用到的目录树", notes = "获取调度需用到的目录树，只能选择layer=3的节点", httpMethod = "POST")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/getDirTreeForScheduler", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<com.alibaba.fastjson.JSONArray> getDirTreeForScheduler() {
        Leaf lf = new Leaf();
        lf = lf.getLeaf(Leaf.CODE_ROOT);
        DirectoryView dv = new DirectoryView(lf);
        com.alibaba.fastjson.JSONObject jsonRoot = new com.alibaba.fastjson.JSONObject();
        jsonRoot.put("code", lf.getCode());
        jsonRoot.put("parentCode", lf.getParentCode());
        jsonRoot.put("name", lf.getName());
        jsonRoot.put("layer", lf.getLayer());
        dv.getTreeForSchedular(lf, jsonRoot);

        com.alibaba.fastjson.JSONArray ary = new com.alibaba.fastjson.JSONArray();
        ary.add(jsonRoot);
        return new Result<>(ary);
    }

    @ApiOperation(value = "根据父节点列出流程目录", notes = "根据父节点列出流程目录", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "typeCode", value = "父节点编码", required = true, dataType = "String"),
            @ApiImplicitParam(name = "pageSize", value = "每页条数", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/listDirByTypeCode", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<JSONObject> listDirByTypeCode(@RequestParam(value="typeCode") String typeCode, @RequestParam(defaultValue = "6") Integer pageSize) {
        com.alibaba.fastjson.JSONArray ary = new com.alibaba.fastjson.JSONArray();
        LeafChildrenCacheMgr lfcm = new LeafChildrenCacheMgr(typeCode);
        int k = 0;
        Vector<Leaf> v = lfcm.getDirList();
        for (Leaf lf : v) {
            DirectoryView dv = new DirectoryView(lf);
            if (lf.isOpen() && dv.canUserSeeWhenInitFlow(request, lf)) {
                JSONObject json = new JSONObject();
                json.put("code", lf.getCode());
                json.put("name", lf.getName());
                ary.add(json);
                k++;
                if (k >= pageSize) {
                    break;
                }
            }
        }
        JSONObject json = new JSONObject();
        json.put("list", ary);
        Config cfg = Config.getInstance();
        json.put("flowOpStyle", StrUtil.toInt(cfg.get("flowOpStyle"), 1));
        return new Result<>(json);
    }

    @ApiOperation(value = "取得目录树，含启用的节点及能够发起的节点", notes = "取得目录树，含启用的节点及能够发起的节点", httpMethod = "POST")
    @ApiImplicitParam(name = "parentCode", value = "父节点编码", required = false, dataType = "String")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/getDirTreeOpened", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<com.alibaba.fastjson.JSONArray> getDirTreeOpened(@RequestParam(value="parentCode", required = false) String parentCode) {
        if (parentCode == null) {
            parentCode = Leaf.CODE_ROOT;
        }
        Leaf lf = new Leaf();
        lf = lf.getLeaf(parentCode);
        DirectoryView dv = new DirectoryView(lf);
        com.alibaba.fastjson.JSONObject jsonRoot = new com.alibaba.fastjson.JSONObject();
        // 如果是根节点，则将code置为空，以免在前台看到是“全部类别”却查不出来引起误解
        jsonRoot.put("code", lf.getCode());
        jsonRoot.put("parentCode", lf.getParentCode());
        jsonRoot.put("name", lf.getName());
        jsonRoot.put("layer", lf.getLayer());
        dv.getTreeOpened(lf, jsonRoot);

        com.alibaba.fastjson.JSONArray ary = new com.alibaba.fastjson.JSONArray();
        ary.add(jsonRoot);
        return new Result<>(ary);
    }

    @ApiOperation(value = "取得目录，仅含大类节点", notes = "取得目录，仅含大类节点", httpMethod = "POST")
    @ApiImplicitParam(name = "parentCode", value = "父节点编码", required = false, dataType = "String")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/getDirTreeOnlyWithType", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<com.alibaba.fastjson.JSONArray> getDirTreeOnlyWithType(@RequestParam(value="parentCode", required = false) String parentCode) {
        if (parentCode == null) {
            parentCode = Leaf.CODE_ROOT;
        }
        Leaf lf = new Leaf();
        lf = lf.getLeaf(parentCode);
        DirectoryView dv = new DirectoryView(lf);
        com.alibaba.fastjson.JSONObject jsonRoot = new com.alibaba.fastjson.JSONObject();
        // 如果是根节点，则将code置为空，以免在前台看到是“全部类别”却查不出来引起误解
        jsonRoot.put("code", lf.getCode());
        jsonRoot.put("parentCode", lf.getParentCode());
        jsonRoot.put("name", lf.getName());
        jsonRoot.put("layer", lf.getLayer());
        dv.getTreeOnlyWithType(lf, jsonRoot);

        com.alibaba.fastjson.JSONArray ary = new com.alibaba.fastjson.JSONArray();
        ary.add(jsonRoot);
        return new Result<>(ary);
    }

    @ApiOperation(value = "获取有权限查询的目录树", notes = "获取有权限查询的目录树", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "parentCode", value = "父节点编码", required = false, dataType = "String"),
            @ApiImplicitParam(name = "isIcon", value = "是否带有图标", required = true, dataType = "Boolean"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/getDirTreeForQuery", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<com.alibaba.fastjson.JSONArray> getDirTreeForQuery(@RequestParam(value="parentCode", required = false) String parentCode, @RequestParam(value="isIcon", defaultValue = "true")Boolean isIcon) {
        if (parentCode == null) {
            parentCode = Leaf.CODE_ROOT;
        }
        Leaf lf = new Leaf();
        lf = lf.getLeaf(parentCode);
        DirectoryView dv = new DirectoryView(lf);
        com.alibaba.fastjson.JSONObject jsonRoot = new com.alibaba.fastjson.JSONObject();
        // 如果是根节点，则将code置为空，以免在前台看到是“全部类别”却查不出来引起误解
        jsonRoot.put("code", lf.getCode());
        jsonRoot.put("parentCode", lf.getParentCode());
        jsonRoot.put("name", lf.getName());
        jsonRoot.put("layer", lf.getLayer());
        dv.getTreeForQuery(lf, jsonRoot, isIcon);

        com.alibaba.fastjson.JSONArray ary = new com.alibaba.fastjson.JSONArray();
        ary.add(jsonRoot);
        return new Result<>(ary);
    }

    @ApiOperation(value = "获取流程中的附件", notes = "获取流程中的附件", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = false, dataType = "Integer"),
            @ApiImplicitParam(name = "actionId", value = "流程节点ID", required = false, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/listAttachment", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> listAttachment(@RequestParam(value = "flowId", required = false) Integer flowId, @RequestParam(value = "actionId", required = false) Integer actionId) {
        if (flowId == null) {
            LogUtil.getLog(getClass()).error("flowId is null");
            return new Result<>(new Vector());
        }

        WorkflowDb wf = new WorkflowDb();
        wf = wf.getWorkflowDb(flowId);
        Leaf lf = new Leaf();
        lf = lf.getLeaf(wf.getTypeCode());
        FormDAO fdao = new FormDAO();
        FormDb fd = new FormDb();
        fd = fd.getFormDb(lf.getFormCode());
        fdao = fdao.getFormDAOByCache(flowId, fd);
        long id = fdao.getId();

        boolean canDocInRed = false, canSeal = false;
        if (actionId != null) {
            WorkflowActionDb wa = new WorkflowActionDb();
            wa = wa.getWorkflowActionDb(actionId);
            canDocInRed = lf.getType() != Leaf.TYPE_FREE && wa.canReceiveRevise();
            canSeal = lf.getType() != Leaf.TYPE_FREE && wa.canSeal();
        }

        int doc_id = wf.getDocId();
        DocumentMgr dm = new DocumentMgr();
        Document doc = dm.getDocument(doc_id);

        java.util.Vector<IAttachment> attachments = new Vector<>();
        if (doc != null) {
            attachments = doc.getAttachments(1);

            // 取模块中添加的附件
            com.redmoon.oa.visual.FormDAO dao = new com.redmoon.oa.visual.FormDAO();
            dao = dao.getFormDAO(id, fd);
            attachments.addAll(dao.getAttachments());

            for (IAttachment am : attachments) {
                am.setCanDocInRed(canDocInRed && am.getCanDocInRed());
                am.setCanSeal(canSeal && am.getCanSeal());
            }
            return new Result<>(attachments);
        }
        else {
            return new Result<>(false, "流程文档不存在");
        }
    }

    @ApiOperation(value = "获取流程中指定字段的附件", notes = "获取流程中指定字段的附件", httpMethod = "POST")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "flowId", value = "流程ID", required = false, dataType = "Integer"),
        @ApiImplicitParam(name = "fieldName", value = "字段名", required = false, dataType = "String ")
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/listAttachmentByField", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<com.alibaba.fastjson.JSONArray> listAttachmentByField(@RequestParam(value = "flowId", required = false) Integer flowId, @RequestParam(value = "fieldName", required = false) String fieldName) {
        try {
            return new Result<>(workflowService.listAttachmentByField(flowId, fieldName));
        } catch (ErrMsgException e) {
            return new Result<>(false, e.getMessage());
        }
    }

    @ApiOperation(value = "发起流程", notes = "发起流程", httpMethod = "POST")
    @ApiImplicitParam(name = "typeCode", value = "流程类型编码", required = true, dataType = "String")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/init", method = {RequestMethod.POST, RequestMethod.GET}, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> init(@RequestParam(value="typeCode") String typeCode) {
        if ("".equals(typeCode) || "not".equals(typeCode)) {
            return new Result<>(false, i18nUtil.get("selectTypeProcess"));
        }

        Leaf lf = new Leaf();
        lf = lf.getLeaf(typeCode);

        if (lf == null) {
            return new Result<>(false, i18nUtil.get("processType"));
        }

        if (lf.getType() == Leaf.TYPE_NONE) {
            return new Result<>(false, i18nUtil.get("selectTypeProcess"));
        }

        LeafPriv lp = new LeafPriv(typeCode);
        if (!lp.canUserSee(authUtil.getUserName())) {
           return new Result<>(false, i18nUtil.get("pvg_invalid"));
        }

        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        String flowTitle = WorkflowMgr.makeTitle(request, privilege, lf);

        WorkflowMgr wm = new WorkflowMgr();
        long startActionId = -1;
        int flowId = -1;

        long projectId = ParamUtil.getLong(request, "projectId", -1);
        int level = ParamUtil.getInt(request, "level", WorkflowDb.LEVEL_NORMAL);

        if (lf.getType() == Leaf.TYPE_FREE) {
            try {
                startActionId = wm.initWorkflowFree(privilege.getUser(request), typeCode, flowTitle, projectId, level);
            } catch (ErrMsgException e) {
                return new Result<>(false, e.getMessage());
            }

            com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
            MyActionDb mad = new MyActionDb();
            mad = mad.getMyActionDb(startActionId);
            flowId = (int)mad.getFlowId();
            json.put("flowId", flowId);
            json.put("myActionId", startActionId);
            json.put("type", Leaf.TYPE_FREE);
            json.put("flowTitle", flowTitle);
            return new Result<>(json);
        } else {
            try {
                startActionId = wm.initWorkflow(privilege.getUser(request), typeCode, flowTitle, projectId, level);
            } catch (ErrMsgException e) {
                // LogUtil.getLog(getClass()).error(e);
                return new Result<>(false, e.getMessage());
            }

            MyActionDb mad = new MyActionDb();
            mad = mad.getMyActionDb(startActionId);
            flowId = (int)mad.getFlowId();
            FormDb fdFlow = new FormDb();
            fdFlow = fdFlow.getFormDb(lf.getFormCode());
            FormDAO fdaoFlow = new FormDAO();
            fdaoFlow = fdaoFlow.getFormDAOByCache(flowId, fdFlow);

            workflowService.onFlowInited(flowId, fdaoFlow);

            // 将request中其它参数也传至url中，表单域选择窗体可能会接收此参数
            String param = "";
            Enumeration paramNames = request.getParameterNames();
            while (paramNames.hasMoreElements()) {
                String paramName = (String) paramNames.nextElement();
                String[] paramValues = request.getParameterValues(paramName);
                if (paramValues.length == 1) {
                    String paramValue = paramValues[0];
                    // 过滤掉formCode
                    if (!"typeCode".equals(paramName)) {
                        param += "&" + paramName + "=" + StrUtil.UrlEncode(paramValue);
                    }
                }
            }

            com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
            if (!"".equals(lf.getParams())) {
                String lfParams = lf.getParams();
                lfParams = lfParams.replaceFirst("\\$userName", StrUtil.UrlEncode(privilege.getUser(request)));
                json.put("extraParams", lfParams);
                param += "&" + lfParams;
            }

            // 催办流程
            if (Leaf.TYPE_CODE_URGE.equals(typeCode)) {
                int flowIdUrged = ParamUtil.getInt(request, "flowIdUrged", -1);
                String userSupervised = ParamUtil.get(request, "userSupervised");
                fdaoFlow.setFieldValue("flow_id", flowIdUrged);
                fdaoFlow.setFieldValue("user_supervised", userSupervised);
                fdaoFlow.save();
            }

            Config cfg = Config.getInstance();
            json.put("myActionId", startActionId);
            json.put("flowId", flowId);
            json.put("flowTitle", flowTitle);
            json.put("params", param);
            json.put("type", Leaf.TYPE_LIST);
            json.put("flowOpStyle", StrUtil.toInt(cfg.get("flowOpStyle"), 1));
            return new Result<>(json);
        }
    }

    @ApiOperation(value = "通过拷贝表单数据发起流程", notes = "通过拷贝表单数据发起流程", httpMethod = "POST")
    @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/initByCopy", method = {RequestMethod.POST, RequestMethod.GET}, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> initByCopy(@RequestParam(value="flowId") Integer flowId) {
        WorkflowDb wf = new WorkflowDb();
        wf = wf.getWorkflowDb(flowId);

        Leaf lf = new Leaf();
        lf = lf.getLeaf(wf.getTypeCode());

        if (lf == null) {
            return new Result<>(false, i18nUtil.get("processType"));
        }

        if (lf.getType() == Leaf.TYPE_NONE) {
            return new Result<>(false, i18nUtil.get("selectTypeProcess"));
        }

        LeafPriv lp = new LeafPriv(wf.getTypeCode());
        if (!lp.canUserSee(authUtil.getUserName())) {
            return new Result<>(false, i18nUtil.get("pvg_invalid"));
        }

        FormDb fdFlow = new FormDb();
        fdFlow = fdFlow.getFormDb(lf.getFormCode());
        FormDAO dao = new FormDAO();
        dao = dao.getFormDAOByCache(flowId, fdFlow);

        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        String flowTitle = WorkflowMgr.makeTitle(request, privilege, lf);

        WorkflowMgr wm = new WorkflowMgr();
        long startActionId = -1;

        long projectId = ParamUtil.getLong(request, "projectId", -1);
        int level = ParamUtil.getInt(request, "level", WorkflowDb.LEVEL_NORMAL);

        if (lf.getType() == Leaf.TYPE_FREE) {
            try {
                startActionId = wm.initWorkflowFree(privilege.getUser(request), wf.getTypeCode(), flowTitle, projectId, level);
            } catch (ErrMsgException e) {
                return new Result<>(false, e.getMessage());
            }

            com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
            MyActionDb mad = new MyActionDb();
            mad = mad.getMyActionDb(startActionId);
            flowId = (int)mad.getFlowId();

            FormDAO fdaoFlow = new FormDAO();
            fdaoFlow = fdaoFlow.getFormDAOByCache(flowId, fdFlow);

            for (FormField ff : dao.getFields()) {
                // 除去文件型控件及意见输入框控件
                if (!"macro_attachments".equals(ff.getMacroType()) && !"macro_image".equals(ff.getMacroType())
                        && !"macro_attachments".equals(ff.getMacroType()) && !"macro_uploader_ctl".equals(ff.getMacroType()) && !"macro_writepad_ctl".equals(ff.getMacroType()) && !"macro_opinion".equals(ff.getMacroType())) {
                    if (ConstUtil.NEST_TABLE.equals(ff.getMacroType()) || ConstUtil.NEST_SHEET.equals(ff.getMacroType()) ) {
                        String defaultVal = StrUtil.decodeJSON(ff.getDescription());
                        org.json.JSONObject jsonObject = null;
                        try {
                            jsonObject = new org.json.JSONObject(defaultVal);
                            String nestFormCode = jsonObject.getString("destForm");

                            // nestFormCode可能会是从模块编码
                            ModuleSetupDb msd = new ModuleSetupDb();
                            msd = msd.getModuleSetupDb(nestFormCode);
                            if (msd == null || !msd.isLoaded()) {
                                LogUtil.getLog(getClass()).error("Module: " + nestFormCode + " is not exist");
                            } else {
                                nestFormCode = msd.getFormCode();
                            }
                            FormDb nestFd = new FormDb();
                            nestFd = nestFd.getFormDb(nestFormCode);
                            List<com.redmoon.oa.visual.FormDAO> list = dao.listNest(nestFormCode, ff.getName());
                            for (com.redmoon.oa.visual.FormDAO nestDao: list) {
                                com.redmoon.oa.visual.FormDAO newDao = new com.redmoon.oa.visual.FormDAO(nestFd);
                                for (FormField ffNest : nestDao.getFields()) {
                                    if (!"macro_attachments".equals(ffNest.getMacroType()) && !"macro_image".equals(ffNest.getMacroType())
                                            && !"macro_attachments".equals(ffNest.getMacroType()) && !"macro_uploader_ctl".equals(ffNest.getMacroType())) {
                                        newDao.setFieldValue(ffNest.getName(), ffNest.getValue());
                                    }
                                }
                                newDao.setFlowId(flowId);
                                newDao.setFlowTypeCode(nestDao.getFlowTypeCode());
                                newDao.setUnitCode(nestDao.getUnitCode());
                                newDao.setCreator(authUtil.getUserName());
                                newDao.setCwsParentForm(nestDao.getCwsParentForm());
                                newDao.setCwsQuoteId(nestDao.getCwsQuoteId());
                                newDao.setCwsNestField(ff.getName());
                                newDao.setCwsId(String.valueOf(fdaoFlow.getId()));
                                newDao.create();
                            }
                        } catch (JSONException | SQLException e) {
                            LogUtil.getLog(getClass()).error(e);
                        }
                    } else {
                        fdaoFlow.setFieldValue(ff.getName(), ff.getValue());
                    }
                }
            }
            fdaoFlow.save();

            json.put("flowId", flowId);
            json.put("myActionId", startActionId);
            json.put("type", Leaf.TYPE_FREE);
            json.put("flowTitle", flowTitle);
            return new Result<>(json);
        } else {
            try {
                startActionId = wm.initWorkflow(privilege.getUser(request), wf.getTypeCode(), flowTitle, projectId, level);
            } catch (ErrMsgException e) {
                return new Result<>(false, e.getMessage());
            }

            MyActionDb mad = new MyActionDb();
            mad = mad.getMyActionDb(startActionId);
            flowId = (int)mad.getFlowId();
            FormDAO fdaoFlow = new FormDAO();
            fdaoFlow = fdaoFlow.getFormDAOByCache(flowId, fdFlow);

            for (FormField ff : dao.getFields()) {
                // 除去文件型控件
                if (!"macro_attachments".equals(ff.getMacroType()) && !"macro_image".equals(ff.getMacroType())
                        && !"macro_attachments".equals(ff.getMacroType()) && !"macro_uploader_ctl".equals(ff.getMacroType()) && !"macro_writepad_ctl".equals(ff.getMacroType()) && !"macro_opinion".equals(ff.getMacroType())) {
                    if (ConstUtil.NEST_TABLE.equals(ff.getMacroType()) || ConstUtil.NEST_SHEET.equals(ff.getMacroType()) ) {
                        String defaultVal = StrUtil.decodeJSON(ff.getDescription());
                        org.json.JSONObject json = null;
                        try {
                            json = new org.json.JSONObject(defaultVal);
                            String nestFormCode = json.getString("destForm");

                            // nestFormCode可能会是从模块编码
                            ModuleSetupDb msd = new ModuleSetupDb();
                            msd = msd.getModuleSetupDb(nestFormCode);
                            if (msd == null || !msd.isLoaded()) {
                                LogUtil.getLog(getClass()).error("Module: " + nestFormCode + " is not exist");
                            } else {
                                nestFormCode = msd.getFormCode();
                            }
                            FormDb nestFd = new FormDb();
                            nestFd = nestFd.getFormDb(nestFormCode);
                            List<com.redmoon.oa.visual.FormDAO> list = dao.listNest(nestFormCode, ff.getName());
                            for (com.redmoon.oa.visual.FormDAO nestDao: list) {
                                com.redmoon.oa.visual.FormDAO newDao = new com.redmoon.oa.visual.FormDAO(nestFd);
                                for (FormField ffNest : nestDao.getFields()) {
                                    if (!"macro_attachments".equals(ffNest.getMacroType()) && !"macro_image".equals(ffNest.getMacroType())
                                            && !"macro_attachments".equals(ffNest.getMacroType()) && !"macro_uploader_ctl".equals(ffNest.getMacroType())) {
                                        newDao.setFieldValue(ffNest.getName(), ffNest.getValue());
                                    }
                                }
                                newDao.setFlowId(flowId);
                                newDao.setFlowTypeCode(nestDao.getFlowTypeCode());
                                newDao.setUnitCode(nestDao.getUnitCode());
                                newDao.setCreator(authUtil.getUserName());
                                newDao.setCwsParentForm(nestDao.getCwsParentForm());
                                newDao.setCwsQuoteId(nestDao.getCwsQuoteId());
                                newDao.setCwsNestField(ff.getName());
                                newDao.setCwsId(String.valueOf(fdaoFlow.getId()));
                                newDao.create();
                            }
                        } catch (JSONException | SQLException e) {
                            LogUtil.getLog(getClass()).error(e);
                        }
                    } else {
                        fdaoFlow.setFieldValue(ff.getName(), ff.getValue());
                    }
                }
            }
            fdaoFlow.save();

            workflowService.onFlowInited(flowId, fdaoFlow);

            // 将request中其它参数也传至url中，表单域选择窗体可能会接收此参数
            String param = "";
            Enumeration paramNames = request.getParameterNames();
            while (paramNames.hasMoreElements()) {
                String paramName = (String) paramNames.nextElement();
                String[] paramValues = request.getParameterValues(paramName);
                if (paramValues.length == 1) {
                    String paramValue = paramValues[0];
                    // 过滤掉formCode
                    if (!"typeCode".equals(paramName)) {
                        param += "&" + paramName + "=" + StrUtil.UrlEncode(paramValue);
                    }
                }
            }

            if (!"".equals(lf.getParams())) {
                String lfParams = lf.getParams();
                lfParams = lfParams.replaceFirst("\\$userName", StrUtil.UrlEncode(privilege.getUser(request)));
                param += "&" + lfParams;
            }

            Config cfg = Config.getInstance();
            com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
            json.put("myActionId", startActionId);
            json.put("flowId", flowId);
            json.put("flowTitle", flowTitle);
            json.put("params", param);
            json.put("type", Leaf.TYPE_LIST);
            json.put("flowOpStyle", StrUtil.toInt(cfg.get("flowOpStyle"), 1));
            return new Result<>(json);
        }
    }

    @ApiOperation(value = "流程列表初始化", notes = "流程列表初始化", httpMethod = "POST")
    @ApiImplicitParam(name = "type", value = "列表类型", required = false, dataType = "String")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/listPage", method = RequestMethod.POST, produces = {"text/html;", "application/json;charset=UTF-8;"})
    public Result<com.alibaba.fastjson.JSONObject> listPage(@RequestParam(value = "type", required = false) String type) {
        com.alibaba.fastjson.JSONObject jsonResult = new com.alibaba.fastjson.JSONObject();

        int displayMode;
        // 显示模式，0表示流程查询、1表示待办、2表示我参与的流程、3表示我发起的流程
        if ("mine".equals(type)) {
            displayMode = WorkflowMgr.DISPLAY_MODE_MINE;
        }
        else if ("attend".equals(type)) {
            displayMode = WorkflowMgr.DISPLAY_MODE_ATTEND;
        }
        else if ("favorite".equals(type)) {
            displayMode = WorkflowMgr.DISPLAY_MODE_FAVORIATE;
        }
        else if ("search".equals(type)) {
            displayMode = WorkflowMgr.DISPLAY_MODE_SEARCH;
        }
        else if ("urge".equals(type)) {
            displayMode = WorkflowMgr.DISPLAY_MODE_URGE;
        }
        else if ("expired".equals(type)) {
            displayMode = WorkflowMgr.DISPLAY_MODE_EXPIRED;
        }
        else {
            // type=doing
            displayMode = WorkflowMgr.DISPLAY_MODE_DOING;
        }

        String action = ParamUtil.get(request, "action"); // sel 选择我的流程

        jsonResult.put("action", action);

        String toa = ParamUtil.get(request, "toa");
        String msg = ParamUtil.get(request, "msg");
        boolean isShowMsg = "ok".equals(toa) && !"".equals(msg);
        if (isShowMsg) {
            jsonResult.put("msg", msg);
        }
        jsonResult.put("isShowMsg", isShowMsg);

        boolean isNav = ParamUtil.getBoolean(request, "isNav", true);
        if (!"sel".equals(action) && isNav) {
            if (displayMode == WorkflowMgr.DISPLAY_MODE_ATTEND || displayMode == WorkflowMgr.DISPLAY_MODE_MINE) {
                isNav = true;
                if (displayMode==WorkflowMgr.DISPLAY_MODE_ATTEND) {
                    jsonResult.put("navIndex", 1);
                }
                else {
                    jsonResult.put("navIndex", 2);
                }
            }
            else {
                isNav = false;
            }
        }
        else {
            isNav = false;
        }
        jsonResult.put("isNav", isNav);
        jsonResult.put("displayMode", displayMode);
        jsonResult.put("DISPLAY_MODE_ATTEND", WorkflowMgr.DISPLAY_MODE_ATTEND);
        jsonResult.put("DISPLAY_MODE_MINE", WorkflowMgr.DISPLAY_MODE_MINE);
        jsonResult.put("DISPLAY_MODE_DOING", WorkflowMgr.DISPLAY_MODE_DOING);
        jsonResult.put("DISPLAY_MODE_FAVORIATE", WorkflowMgr.DISPLAY_MODE_FAVORIATE);
        jsonResult.put("DISPLAY_MODE_SEARCH", WorkflowMgr.DISPLAY_MODE_SEARCH);

        Config cfgTop = Config.getInstance();
        jsonResult.put("flowPerformanceDisplay", cfgTop.getBooleanProperty("flowPerformanceDisplay"));
        jsonResult.put("flowOpStyle", StrUtil.toInt(cfgTop.get("flowOpStyle"), 1));

        com.redmoon.oa.flow.FlowConfig conf = new com.redmoon.oa.flow.FlowConfig();
        com.redmoon.oa.pvg.Privilege pvg = new com.redmoon.oa.pvg.Privilege();
        jsonResult.put("isZipBtnShow", pvg.isUserPrivValid(request, "flow.zip.batch") && conf.getIsDisplay("FLOW_BUTTON_ZIP"));

        String typeCode = ParamUtil.get(request, "typeCode");
        String title = ParamUtil.get(request, "title");
        // 发起人
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        String myUserName = privilege.getUser(request);
        jsonResult.put("myUserName", myUserName);

        com.alibaba.fastjson.JSONArray colProps = null;
        Leaf colLeaf = new Leaf();
        FormDb fd = new FormDb();
        if (!"".equals(typeCode)) {
            colLeaf = colLeaf.getLeaf(typeCode);
            if (colLeaf == null) {
                return new Result<>(false, "流程类型 typeCode: " + typeCode + "不存在");
            }
            fd = fd.getFormDb(colLeaf.getFormCode());
        } else {
            colLeaf = colLeaf.getLeaf(Leaf.CODE_ROOT);
        }

        if (colLeaf.isLoaded() && !StrUtil.isEmpty(colLeaf.getColProps())) {
            colProps = com.alibaba.fastjson.JSONArray.parseArray(colLeaf.getColProps());
        }
        if (colProps == null || colProps.size() == 0) {
            colProps = com.redmoon.oa.flow.Leaf.getDefaultColProps(request, typeCode, displayMode);
        }
        // 因为可以勾选导出，故此处注释掉
        // 如果是待办流程，且允许批处理，则加上复选框列
        /*if (displayMode == WorkflowMgr.DISPLAY_MODE_DOING && cfgTop.getBooleanProperty("canFlowDisposeBatch")) {
            jsonResult.put("isCheckbox", true);
        }
        else {
            jsonResult.put("isCheckbox", false);
        }*/

        Leaf leaf = new Leaf();
        if (!"".equals(typeCode)) {
            leaf = leaf.getLeaf(typeCode);
            if (!leaf.isLoaded()) {
                return new Result<>(false, i18nUtil.get("selectTypeProcess"));
            }
        }

        /*if (displayMode==WorkflowMgr.DISPLAY_MODE_SEARCH && "".equals(typeCode)) {
            Result<com.alibaba.fastjson.JSONObject> result = new Result<>(false, i18nUtil.get("selectTypeProcess"));
            return result;
        }*/

        jsonResult.put("colProps", colProps);

        if (displayMode == WorkflowMgr.DISPLAY_MODE_SEARCH) {
            LeafPriv leafPriv = null;
            boolean canQuery = false;
            // 如果没有选节点
            if ("".equals(typeCode)) {
                // 如果总查询的权限（即根目录节点上有查询权限），则可以列出全部的记录
                canQuery = LeafPriv.canUserQueryAll(request);
                if (!canQuery) {
                    return new Result<>(false, i18nUtil.get("selectTypeProcess"));
                }
            } else {
                // 如果是分类节点，且用户不是管理员权限
                if (leaf.getType() == Leaf.TYPE_NONE) {
                    canQuery = LeafPriv.canUserQueryAll(request);
                    if (!canQuery) {
                        Vector<Leaf> v = new Vector<>();
                        try {
                            v = leaf.getAllChild(v, leaf);
                            for (Leaf sl : v) {
                                leafPriv = new LeafPriv(sl.getCode());
                                // 如果分类节点的某个子节点无查询权限则退出
                                if (!leafPriv.canUserQuery(myUserName)) {
                                    return new Result<>(false, i18nUtil.get("selectTypeProcess"));
                                }
                            }
                            canQuery = true;
                        } catch (ErrMsgException e) {
                            LogUtil.getLog(getClass()).error(e);
                        }
                    }
                } else {
                    leafPriv = new LeafPriv(typeCode);
                    canQuery = leafPriv.canUserQuery(myUserName);
                }
            }

            if (!canQuery) {
                return new Result<>(false, i18nUtil.get("permissionsIllegal"));
            }
        }

        jsonResult.put("flowTypeCode", typeCode);
        jsonResult.put("isFlowTypeSelected", !"".equals(typeCode));
        jsonResult.put("flowTitle", title);

        jsonResult.put("STATUS_NOT_STARTED", WorkflowDb.STATUS_NOT_STARTED);
        jsonResult.put("STATUS_STARTED", WorkflowDb.STATUS_STARTED);
        jsonResult.put("STATUS_FINISHED", WorkflowDb.STATUS_FINISHED);
        jsonResult.put("STATUS_DISCARDED", WorkflowDb.STATUS_DISCARDED);
        jsonResult.put("STATUS_REFUSED", WorkflowDb.STATUS_REFUSED);

        jsonResult.put("STATUS_NOT_STARTED_DESC", WorkflowDb.getStatusDesc(WorkflowDb.STATUS_NOT_STARTED));
        jsonResult.put("STATUS_STARTED_DESC", WorkflowDb.getStatusDesc(WorkflowDb.STATUS_STARTED));
        jsonResult.put("STATUS_FINISHED_DESC", WorkflowDb.getStatusDesc(WorkflowDb.STATUS_FINISHED));
        jsonResult.put("STATUS_DISCARDED_DESC", WorkflowDb.getStatusDesc(WorkflowDb.STATUS_DISCARDED));
        jsonResult.put("STATUS_REFUSED_DESC", WorkflowDb.getStatusDesc(WorkflowDb.STATUS_REFUSED));

        jsonResult.put("TYPE_DATE", FormField.TYPE_DATE);
        jsonResult.put("TYPE_MACRO", FormField.TYPE_MACRO);
        jsonResult.put("TYPE_NUMBERIC", "numberic");
        jsonResult.put("TYPE_SELECT", FormField.TYPE_SELECT);
        jsonResult.put("TYPE_RADIO", FormField.TYPE_RADIO);
        jsonResult.put("TYPE_CHECKBOX", FormField.TYPE_CHECKBOX);

        ArrayList<String> list = new ArrayList<String>();

        boolean isCondProps = false;
        if (leaf.isLoaded() && !"".equals(leaf.getCondProps())) {
            isCondProps = true;
            com.alibaba.fastjson.JSONObject json = com.alibaba.fastjson.JSONObject.parseObject(leaf.getCondProps());
            MacroCtlMgr mm = new MacroCtlMgr();
            Map<String, String> checkboxGroupMap = new HashMap<String, String>();

            com.alibaba.fastjson.JSONArray ary = new com.alibaba.fastjson.JSONArray();
            String condFields = (String) json.get("fields");
            String[] fieldAry = condFields.split(",");
            for (String fieldName : fieldAry) {
                boolean isSub = false;
                FormDb subFormDb = null;
                FormField ff = null;
                String fieldTitle;
                String condType = (String) json.get(fieldName);
                String queryValue = ParamUtil.get(request, fieldName);

                com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject();

                ff = fd.getFormField(fieldName);
                if (ff == null) {
                    jsonObject.put("fieldTitle", fieldName + "不存在");
                    continue;
                }
                if (ff.getType().equals(FormField.TYPE_CHECKBOX)) {
                    String desc = StrUtil.getNullStr(ff.getDescription());
                    fieldTitle = ff.getTitle();
                    /*if (!"".equals(desc)) {
                        fieldTitle = desc;
                    } else {
                        fieldTitle = ff.getTitle();
                    }*/
                    String chkGroup = desc;
                    if (!"".equals(chkGroup)) {
                        if (!checkboxGroupMap.containsKey(chkGroup)) {
                            checkboxGroupMap.put(chkGroup, "");
                        } else {
                            continue;
                        }
                    }
                } else {
                    fieldTitle = ff.getTitle();
                }

                jsonObject.put("fieldTitle", fieldTitle);
                jsonObject.put("fieldName", fieldName);
                jsonObject.put("queryValue", queryValue);
                jsonObject.put("condType", condType);
                jsonObject.put("type", ff.getType());

                // 用于给convertToHTMLCtlForQuery辅助传值
                ff.setCondType(condType);
                if (ff.getType().equals(FormField.TYPE_DATE) || ff.getType().equals(FormField.TYPE_DATE_TIME)) {
                    jsonObject.put("typeOfField", FormField.TYPE_DATE);
                    if ("0".equals(condType)) {
                        String fDate = ParamUtil.get(request, ff.getName() + "FromDate");
                        String tDate = ParamUtil.get(request, ff.getName() + "ToDate");
                        jsonObject.put("fromDate", fDate);
                        jsonObject.put("toDate", tDate);
                        list.add(ff.getName() + "FromDate");
                        list.add(ff.getName() + "ToDate");
                    } else {
                        list.add(ff.getName());
                    }
                } else if (ff.getType().equals(FormField.TYPE_MACRO)) {
                    jsonObject.put("typeOfField", FormField.TYPE_MACRO);

                    MacroCtlUnit mu = mm.getMacroCtlUnit(ff.getMacroType());
                    if (mu == null) {
                        DebugUtil.e(getClass(), "listPage", ff.getTitle() + " 宏控件: " + ff.getMacroType() + " 不存在");
                    } else {
                        String queryValueRealShow = ParamUtil.get(request, fieldName + "_realshow");
                        // 用main及other映射字段的描述替换其name，以使得生成的查询控件的id及name中带有main及other
                        FormField ffQuery = null;
                        try {
                            ffQuery = (FormField) ff.clone();
                            ffQuery.setName(fieldName);
                            IFormMacroCtl ifmc = mu.getIFormMacroCtl();
                            jsonObject.put("ctlForQuery", ifmc.convertToHTMLCtlForQuery(request, ffQuery));
                            jsonObject.put("queryValueRealShow", queryValueRealShow);
                            boolean isJudgeEmpty = false;
                            if ("text".equals(ifmc.getControlType()) || "img".equals(ifmc.getControlType()) || "textarea".equals(ifmc.getControlType())) {
                                isJudgeEmpty = true;
                            }
                            jsonObject.put("isJudgeEmpty", isJudgeEmpty);
                        } catch (CloneNotSupportedException e) {
                            LogUtil.getLog(getClass()).error(e);
                        }
                    }
                } else if (FormField.isNumeric(ff.getFieldType())) {
                    jsonObject.put("typeOfField", "numberic");

                    String nameCond = ParamUtil.get(request, fieldName + "_cond");
                    if ("".equals(nameCond)) {
                        nameCond = condType;
                    }
                    jsonObject.put("nameCond", nameCond);
                } else {
                    jsonObject.put("typeOfField", "others");

                    boolean isSpecial = false;
                    if (condType.equals(SQLBuilder.COND_TYPE_NORMAL)) {
                        if (ff.getType().equals(FormField.TYPE_SELECT)) {
                            isSpecial = true;
                            // jsonObject.put("options", FormParser.getOptionsOfSelect(fd, ff));
                            StringBuilder sb = new StringBuilder();
                            Map<String, String> map = ff.getOptions();
                            for (String val : map.keySet()) {
                                String text = map.get(val);
                                sb.append("<option value='" + val + "'>" + text + "</option>");
                            }
                            jsonObject.put("options", sb.toString());
                        }
                    } else if (ff.getType().equals(FormField.TYPE_RADIO)) {
                        isSpecial = true;
                        com.alibaba.fastjson.JSONArray aryR = new com.alibaba.fastjson.JSONArray();
                        String[][] aryRadio = FormParser.getOptionsArrayOfRadio(fd, ff);
                        for (String[] strings : aryRadio) {
                            String val = strings[0];
                            String text = strings[1];
                            com.alibaba.fastjson.JSONObject jsonR = new com.alibaba.fastjson.JSONObject();
                            jsonR.put("val", val);
                            jsonR.put("text", text);
                            aryR.add(jsonR);
                        }
                        jsonObject.put("aryRadio", aryR);
                    } else if (ff.getType().equals(FormField.TYPE_CHECKBOX)) {
                        isSpecial = true;
                        String[][] aryChk = null;
                        if (isSub) {
                            aryChk = FormParser.getOptionsArrayOfCheckbox(subFormDb, ff);
                        } else {
                            aryChk = FormParser.getOptionsArrayOfCheckbox(fd, ff);
                        }
                        com.alibaba.fastjson.JSONArray aryCh = new com.alibaba.fastjson.JSONArray();
                        for (String[] strings : aryChk) {
                            String val = strings[0];
                            String fName = strings[1];
                            if (isSub) {
                                fName = "sub:" + subFormDb.getCode() + ":" + fName;
                            }
                            String text = strings[2];
                            queryValue = ParamUtil.get(request, fName);

                            com.alibaba.fastjson.JSONObject jsonChk = new com.alibaba.fastjson.JSONObject();
                            jsonChk.put("val", val);
                            jsonChk.put("fieldName", fName);
                            jsonChk.put("queryValue", queryValue);
                            jsonChk.put("text", text);
                            aryCh.add(jsonChk);
                        }
                        jsonObject.put("aryChk", aryCh);
                    }

                    jsonObject.put("isSpecial", isSpecial);
                }
                ary.add(jsonObject);
            }
            jsonResult.put("aryCond", ary);
        }

        jsonResult.put("dateFieldList", list);
        jsonResult.put("isCondProps", isCondProps);
        jsonResult.put("IS_EMPTY", SQLBuilder.IS_EMPTY);
        jsonResult.put("IS_NOT_EMPTY", SQLBuilder.IS_NOT_EMPTY);
        jsonResult.put("COND_TYPE_NORMAL", SQLBuilder.COND_TYPE_NORMAL);

        boolean canDisposeBatch = false;
        com.redmoon.oa.Config cfg = com.redmoon.oa.Config.getInstance();

        ILicenseUtil licenseUtil = SpringUtil.getBean(ILicenseUtil.class);
        if (licenseUtil.isEnabled(LicenseConstUtil.APPROVE_BATCH)) {
            if (displayMode==WorkflowMgr.DISPLAY_MODE_DOING && cfg.getBooleanProperty("canFlowDisposeBatch")) {
                if (privilege.isUserPrivValid(request, "flow.batch")) {
                    canDisposeBatch = true;
                }
            }
        }

        /*boolean isExport = false;
        if (displayMode==WorkflowMgr.DISPLAY_MODE_SEARCH) {
            if (leaf != null && leaf.isLoaded() && leaf.getType() != Leaf.TYPE_NONE) {
                isExport = true;
            }
        }*/
        boolean isExport = true;

        // 流程节点类型
        jsonResult.put("nodeType", leaf.getType());

        boolean isFlowManager = false;
        if (privilege.isUserPrivValid(request, "admin")) {
            isFlowManager = true;
        } else {
            if (privilege.isUserPrivValid(request, "admin.flow")) {
                if (!"".equals(typeCode)) {
                    LeafPriv lp = new LeafPriv(typeCode);
                    if (lp.canUserExamine(privilege.getUser(request))) {
                        isFlowManager = true;
                    }
                }
            }
        }
        jsonResult.put("isFlowManager", isFlowManager);

        boolean isAdmin = privilege.isUserPrivValid(request, "admin");
        // boolean isButtons = canDisposeBatch || isExport || isAdmin;
        jsonResult.put("isAdmin", isAdmin);
        jsonResult.put("isExport", isExport);
        jsonResult.put("canDisposeBatch", canDisposeBatch);
        jsonResult.put("curUserName", privilege.getUser(request));
        jsonResult.put("STATE_RETURN", WorkflowActionDb.STATE_RETURN);
        jsonResult.put("STATE_DOING", WorkflowActionDb.STATE_DOING);
        jsonResult.put("STATE_RETURN_NAME", WorkflowActionDb.getStatusName(WorkflowActionDb.STATE_RETURN));
        jsonResult.put("STATE_DOING_NAME", WorkflowActionDb.getStatusName(WorkflowActionDb.STATE_DOING));

        boolean isBtnAttentionShow = conf.getIsDisplay("FLOW_BUTTON_ATTENTION");
        jsonResult.put("isBtnAttentionShow", isBtnAttentionShow);
        return new Result<>(jsonResult);
    }

    @ApiOperation(value = "取得某流程类型的查询条件", notes = "取得某流程类型的查询条件", httpMethod = "POST")
    @ApiImplicitParam(name = "typeCode", value = "流程类型", required = false, dataType = "String")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/getConds", method=RequestMethod.GET)
    public Result<Object> getConds(@RequestParam(value = "typeCode", required = false) String typeCode) {
        Leaf leaf = new Leaf();
        if (!"".equals(typeCode)) {
            leaf = leaf.getLeaf(typeCode);
            if (!leaf.isLoaded()) {
                return new Result<>(false, i18nUtil.get("selectTypeProcess"));
            }
        }

        FormDb fd = new FormDb();
        fd = fd.getFormDb(leaf.getFormCode());

        com.alibaba.fastjson.JSONObject jsonResult = new com.alibaba.fastjson.JSONObject();
        com.alibaba.fastjson.JSONArray ary = new com.alibaba.fastjson.JSONArray();
        boolean isCondProps = false;
        if (leaf.isLoaded() && !"".equals(leaf.getCondProps())) {
            isCondProps = true;
            com.alibaba.fastjson.JSONObject json = com.alibaba.fastjson.JSONObject.parseObject(leaf.getCondProps());
            MacroCtlMgr mm = new MacroCtlMgr();
            Map<String, String> checkboxGroupMap = new HashMap<String, String>();

            String condFields = (String) json.get("fields");
            String[] fieldAry = StrUtil.split(condFields, ",");
            if (fieldAry == null) {
                fieldAry = new String[]{};
            }
            for (String fieldName : fieldAry) {
                boolean isSub = false;
                FormDb subFormDb = null;
                String fieldTitle;
                String condType = (String) json.get(fieldName);
                String queryValue = ParamUtil.get(request, fieldName);

                com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject();

                FormField ff = fd.getFormField(fieldName);
                if (ff == null) {
                    jsonObject.put("fieldTitle", fieldName + "不存在");
                    continue;
                }
                if (ff.getType().equals(FormField.TYPE_CHECKBOX)) {
                    String desc = StrUtil.getNullStr(ff.getDescription());
                    /*if (!"".equals(desc)) {
                        fieldTitle = desc;
                    } else {
                        fieldTitle = ff.getTitle();
                    }*/
                    fieldTitle = ff.getTitle();
                    String chkGroup = desc;
                    if (!"".equals(chkGroup)) {
                        if (!checkboxGroupMap.containsKey(chkGroup)) {
                            checkboxGroupMap.put(chkGroup, "");
                        } else {
                            continue;
                        }
                    }
                } else {
                    fieldTitle = ff.getTitle();
                }

                jsonObject.put("fieldTitle", fieldTitle);
                jsonObject.put("fieldName", fieldName);
                jsonObject.put("queryValue", queryValue);
                jsonObject.put("condType", condType);
                jsonObject.put("type", ff.getType());

                // 用于给convertToHTMLCtlForQuery辅助传值
                ff.setCondType(condType);
                if (ff.getType().equals(FormField.TYPE_DATE) || ff.getType().equals(FormField.TYPE_DATE_TIME)) {
                    jsonObject.put("typeOfField", FormField.TYPE_DATE);
                    if ("0".equals(condType)) {
                        String fDate = ParamUtil.get(request, ff.getName() + "FromDate");
                        String tDate = ParamUtil.get(request, ff.getName() + "ToDate");
                        jsonObject.put("fromDate", fDate);
                        jsonObject.put("toDate", tDate);
                    }
                } else if (ff.getType().equals(FormField.TYPE_MACRO)) {
                    jsonObject.put("typeOfField", FormField.TYPE_MACRO);

                    MacroCtlUnit mu = mm.getMacroCtlUnit(ff.getMacroType());
                    if (mu == null) {
                        DebugUtil.e(getClass(), "getConds", ff.getTitle() + " 宏控件: " + ff.getMacroType() + " 不存在");
                    } else {
                        String queryValueRealShow = ParamUtil.get(request, fieldName + "_realshow");
                        // 用main及other映射字段的描述替换其name，以使得生成的查询控件的id及name中带有main及other
                        FormField ffQuery = null;
                        try {
                            ffQuery = (FormField) ff.clone();
                            ffQuery.setName(fieldName);
                            IFormMacroCtl ifmc = mu.getIFormMacroCtl();
                            jsonObject.put("ctlForQuery", ifmc.convertToHTMLCtlForQuery(request, ffQuery));
                            jsonObject.put("queryValueRealShow", queryValueRealShow);
                            boolean isJudgeEmpty = false;
                            if ("text".equals(ifmc.getControlType()) || "img".equals(ifmc.getControlType()) || "textarea".equals(ifmc.getControlType())) {
                                isJudgeEmpty = true;
                            }
                            jsonObject.put("isJudgeEmpty", isJudgeEmpty);
                        } catch (CloneNotSupportedException e) {
                            LogUtil.getLog(getClass()).error(e);
                        }
                    }
                } else if (FormField.isNumeric(ff.getFieldType())) {
                    jsonObject.put("typeOfField", "numberic");

                    String nameCond = ParamUtil.get(request, fieldName + "_cond");
                    if ("".equals(nameCond)) {
                        nameCond = condType;
                    }
                    jsonObject.put("nameCond", nameCond);
                } else {
                    jsonObject.put("typeOfField", "others");

                    boolean isSpecial = false;
                    if (condType.equals(SQLBuilder.COND_TYPE_NORMAL)) {
                        if (ff.getType().equals(FormField.TYPE_SELECT)) {
                            isSpecial = true;
                            // jsonObject.put("options", FormParser.getOptionsOfSelect(fd, ff));
                            StringBuilder sb = new StringBuilder();
                            Map<String, String> map = ff.getOptions();
                            for (String val : map.keySet()) {
                                String text = map.get(val);
                                sb.append("<option value='" + val + "'>" + text + "</option>");
                            }
                            jsonObject.put("options", sb.toString());
                        }
                        else if (ff.getType().equals(FormField.TYPE_RADIO)) {
                            isSpecial = true;
                            com.alibaba.fastjson.JSONArray aryR = new com.alibaba.fastjson.JSONArray();
                            String[][] aryRadio = FormParser.getOptionsArrayOfRadio(fd, ff);
                            for (String[] strings : aryRadio) {
                                String val = strings[0];
                                String text = strings[1];
                                com.alibaba.fastjson.JSONObject jsonR = new com.alibaba.fastjson.JSONObject();
                                jsonR.put("val", val);
                                jsonR.put("text", text);
                                aryR.add(jsonR);
                            }
                            jsonObject.put("aryRadio", aryR);
                        }
                    } else if (ff.getType().equals(FormField.TYPE_RADIO)) {
                        isSpecial = true;
                        com.alibaba.fastjson.JSONArray aryR = new com.alibaba.fastjson.JSONArray();
                        String[][] aryRadio = FormParser.getOptionsArrayOfRadio(fd, ff);
                        for (String[] strings : aryRadio) {
                            String val = strings[0];
                            String text = strings[1];
                            com.alibaba.fastjson.JSONObject jsonR = new com.alibaba.fastjson.JSONObject();
                            jsonR.put("val", val);
                            jsonR.put("text", text);
                            aryR.add(jsonR);
                        }
                        jsonObject.put("aryRadio", aryR);
                    } else if (ff.getType().equals(FormField.TYPE_CHECKBOX)) {
                        isSpecial = true;
                        String[][] aryChk = null;
                        if (isSub) {
                            aryChk = FormParser.getOptionsArrayOfCheckbox(subFormDb, ff);
                        } else {
                            aryChk = FormParser.getOptionsArrayOfCheckbox(fd, ff);
                        }
                        com.alibaba.fastjson.JSONArray aryCh = new com.alibaba.fastjson.JSONArray();
                        for (String[] strings : aryChk) {
                            String val = strings[0];
                            String fName = strings[1];
                            if (isSub) {
                                fName = "sub:" + subFormDb.getCode() + ":" + fName;
                            }
                            String text = strings[2];
                            queryValue = ParamUtil.get(request, fName);

                            com.alibaba.fastjson.JSONObject jsonChk = new com.alibaba.fastjson.JSONObject();
                            jsonChk.put("val", val);
                            jsonChk.put("fieldName", fName);
                            jsonChk.put("queryValue", queryValue);
                            jsonChk.put("text", text);
                            aryCh.add(jsonChk);
                        }
                        jsonObject.put("aryChk", aryCh);
                    }

                    jsonObject.put("isSpecial", isSpecial);
                }
                ary.add(jsonObject);
            }
        }
        jsonResult.put("aryCond", ary);
        jsonResult.put("isCondProps", isCondProps);
        return new Result<>(jsonResult);
    }

    @ApiOperation(value = "置某流程类型的查询条件", notes = "置某流程类型的查询条件", httpMethod = "POST")
    @ApiImplicitParam(name = "typeCode", value = "流程类型", required = true, dataType = "String")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/setConds", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> setConds(@RequestParam(value="typeCode") String typeCode) {
        Leaf lf = new Leaf();
        lf = lf.getLeaf(typeCode);
        if (lf == null) {
            return new Result<>(false, cn.js.fan.web.SkinUtil.LoadString(request, "err_id"));
        }

        int isToolbar = ParamUtil.getInt(request, "isToolbar", 0);

        String[] queryFields = ParamUtil.getParameters(request, "queryFields");
        if (queryFields==null) {
            queryFields = new String[]{};
        }
        JSONObject json = new JSONObject();
        // 因为json是无序的，所以需要用fields记录条件字段的顺序
        String fields = "";
        for (String queryField : queryFields) {
            String token = ParamUtil.get(request, queryField + "_cond");
            json.put(queryField, token);
            if ("".equals(fields)) {
                fields = queryField;
            } else {
                fields += "," + queryField;
            }
        }
        json.put("fields", fields);
        json.put("isToolbar", isToolbar);

        lf.setCondProps(json.toString());
        return new Result<>(lf.update());
    }

    @ApiOperation(value = "取表单中的字段", notes = "取表单中的字段", httpMethod = "POST")
    @ApiImplicitParam(name = "typeCode", value = "流程类型", required = true, dataType = "String")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/getFields", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> getFields(@RequestParam(value = "typeCode") String typeCode) {
        Leaf lf = new Leaf();
        lf = lf.getLeaf(typeCode);
        if (lf == null) {
            return new Result<>(false, "流程类型:" + typeCode + "不存在");
        }
        FormDb fd = new FormDb();
        fd = fd.getFormDb(lf.getFormCode());
        if (!fd.isLoaded()) {
            return new Result<>(false, "表单不存在");
        }
        return new Result<>(fd.getFields());
    }

    @ApiOperation(value = "取表单中的嵌套表格的字段", notes = "取表单中的嵌套表格的字段", httpMethod = "POST")
    @ApiImplicitParam(name = "typeCode", value = "流程类型", required = true, dataType = "String")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/getNestFields", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> getNestFields(@RequestParam(value = "typeCode") String typeCode) {
        Leaf lf = new Leaf();
        lf = lf.getLeaf(typeCode);
        if (lf == null) {
            return new Result<>(false, "流程类型:" + typeCode + "不存在");
        }
        FormDb fd = new FormDb();
        fd = fd.getFormDb(lf.getFormCode());
        if (!fd.isLoaded()) {
            return new Result<>(false, "表单不存在");
        }
        JSONArray arr = new JSONArray();

        // 取嵌套表格字段
        MacroCtlMgr mm = new MacroCtlMgr();
        Iterator<FormField> ir = fd.getFields().iterator();
        while (ir.hasNext()) {
            FormField ff = ir.next();
            if (ff.getType().equals(FormField.TYPE_MACRO)) {
                MacroCtlUnit mu = mm.getMacroCtlUnit(ff.getMacroType());
                if (mu.getNestType() != MacroCtlUnit.NEST_TYPE_NONE) {
                    String nestFormCode = ff.getDescription();
                    try {
                        String defaultVal = StrUtil.decodeJSON(nestFormCode);
                        org.json.JSONObject json = new org.json.JSONObject(defaultVal);
                        nestFormCode = json.getString("destForm");
                    } catch (JSONException e) {
                        LogUtil.getLog(getClass()).info(fd.getName() + ": " + ff.getName() + " " + nestFormCode + " is old version before 20131123. ff.getDefaultValueRaw()=" + ff.getDefaultValueRaw());
                    }

                    FormDb fdNest = new FormDb();
                    fdNest = fdNest.getFormDb(nestFormCode);
                    JSONArray arrNest = (JSONArray)JSON.toJSON(fdNest.getFields());
                    arr.addAll(arrNest);
                }
            }
        }

        return new Result<>(arr);
    }

    @ApiOperation(value = "取表单及嵌套表中的字段，用于设置流程节点可写及隐藏字段", notes = "取表单及嵌套表中的字段，用于设置流程节点可写及隐藏字段", httpMethod = "POST")
    @ApiImplicitParam(name = "formCode", value = "表单编码", required = true, dataType = "String")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/getFieldsWithNestForNode", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> getFieldsWithNestForNode(@RequestParam(value = "formCode") String formCode) {
        FormDb fd = new FormDb();
        fd = fd.getFormDb(formCode);
        if (!fd.isLoaded()) {
            return new Result<>(false, "表单: " + formCode + " 不存在");
        }
        Vector<FormField> v = fd.getFields();
        JSONArray arr = (JSONArray)JSON.toJSON(v);

        // 去除其中的嵌套表格字段
        MacroCtlMgr mm = new MacroCtlMgr();
        Iterator<FormField> ir = v.iterator();
        while (ir.hasNext()) {
            FormField ff = ir.next();
            if (ff.getType().equals(FormField.TYPE_MACRO)) {
                MacroCtlUnit mu = mm.getMacroCtlUnit(ff.getMacroType());
                if (mu == null) {
                    LogUtil.getLog(getClass()).error("表单: " + fd.getName() + " 中字段: " + ff.getTitle() + " 对应的宏控件: " + ff.getMacroType() + " 类型不存在");
                    continue;
                }
                if (mu.getNestType() != MacroCtlUnit.NEST_TYPE_NONE) {
                    ir.remove();

                    String nestFormCode = ff.getDescription();
                    try {
                        String defaultVal = StrUtil.decodeJSON(nestFormCode);
                        org.json.JSONObject json = new org.json.JSONObject(defaultVal);
                        if (json.has("destForm")) {
                            nestFormCode = json.getString("destForm");
                        } else {
                            // 嵌套表单控件中只有formCode
                            nestFormCode = json.getString("formCode");
                        }
                    } catch (JSONException e) {
                        LogUtil.getLog(getClass()).info(fd.getName() + ": " + ff.getName() + " " + nestFormCode + " is old version before 20131123. ff.getDefaultValueRaw()=" + ff.getDefaultValueRaw());
                    }

                    ModuleSetupDb msd = new ModuleSetupDb();
                    msd = msd.getModuleSetupDbOrInit(nestFormCode);

                    FormDb fdNest = new FormDb();
                    fdNest = fdNest.getFormDb(msd.getFormCode());
                    JSONArray arrNest = (JSONArray)JSON.toJSON(fdNest.getFields());
                    for (Object o : arrNest) {
                        JSONObject jsonObject = (JSONObject)o;
                        jsonObject.put("isNest", true);
                        jsonObject.put("name", "nest." + jsonObject.getString("name"));
                        jsonObject.put("title", ff.getTitle() + "=>" + jsonObject.getString("title"));
                    }
                    arr.addAll(arrNest);
                }
            }
        }

        return new Result<>(arr);
    }

    @ApiOperation(value = "取表单及嵌套表中的字段，用于流程查询中的导出", notes = "取表单及嵌套表中的字段，用于流程查询中的导出", httpMethod = "POST")
    @ApiImplicitParam(name = "typeCode", value = "流程类型", required = true, dataType = "String")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/getFieldsWithNest", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> getFieldsWithNest(@RequestParam(value = "typeCode") String typeCode) {
        Leaf lf = new Leaf();
        lf = lf.getLeaf(typeCode);
        if (lf == null) {
            return new Result<>(false, "流程类型:" + typeCode + "不存在");
        }
        FormDb fd = new FormDb();
        fd = fd.getFormDb(lf.getFormCode());
        if (!fd.isLoaded()) {
            return new Result<>(false, "表单不存在");
        }
        Vector<FormField> v = fd.getFields();
        JSONArray arr = (JSONArray)JSON.toJSON(v);

        // 去除其中的嵌套表格字段
        MacroCtlMgr mm = new MacroCtlMgr();
        Iterator<FormField> ir = v.iterator();
        while (ir.hasNext()) {
            FormField ff = ir.next();
            if (ff.getType().equals(FormField.TYPE_MACRO)) {
                MacroCtlUnit mu = mm.getMacroCtlUnit(ff.getMacroType());
                if (mu.getNestType() != MacroCtlUnit.NEST_TYPE_NONE) {
                    ir.remove();

                    String nestFormCode = ff.getDescription();
                    try {
                        String defaultVal = StrUtil.decodeJSON(nestFormCode);
                        org.json.JSONObject json = new org.json.JSONObject(defaultVal);
                        nestFormCode = json.getString("destForm");
                    } catch (JSONException e) {
                        LogUtil.getLog(getClass()).info(fd.getName() + ": " + ff.getName() + " " + nestFormCode + " is old version before 20131123. ff.getDefaultValueRaw()=" + ff.getDefaultValueRaw());
                    }

                    FormDb fdNest = new FormDb();
                    fdNest = fdNest.getFormDb(nestFormCode);
                    JSONArray arrNest = (JSONArray)JSON.toJSON(fdNest.getFields());
                    for (Object o : arrNest) {
                        JSONObject jsonObject = (JSONObject)o;
                        jsonObject.put("isNest", true);
                        jsonObject.put("name", fdNest.getCode() + ":" + jsonObject.getString("name"));
                        jsonObject.put("title", fdNest.getName() + ":" + jsonObject.getString("title"));
                    }
                    arr.addAll(arrNest);
                }
            }
        }

        return new Result<>(arr);
    }

    @ApiOperation(value = "查看流程详情", notes = "查看流程详情", httpMethod = "POST")
    @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flowShow", method={RequestMethod.GET, RequestMethod.POST})
    public Result<com.alibaba.fastjson.JSONObject> flowShow(@RequestParam(value="flowId") Integer flowId) {
        WorkflowMgr wfm = new WorkflowMgr();
        WorkflowDb wf = wfm.getWorkflowDb(flowId);

        if (!wf.isLoaded()) {
            return new Result<>(false, i18nUtil.get("processNotExist"));
        }

        boolean isFlowManager = false;
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        LeafPriv lp = new LeafPriv(wf.getTypeCode());
        if (privilege.isUserPrivValid(request, ConstUtil.PRIV_ADMIN)) {
            isFlowManager = true;
        } else {
            if (lp.canUserExamine(privilege.getUser(request))) {
                isFlowManager = true;
            }
        }

        if (wf.getStatus() == WorkflowDb.STATUS_DELETED) {
            if (!isFlowManager) {
                return new Result<>(false, "流程已被删除");
            }
        }

        // 检查
        Leaf lf = new Leaf();
        lf = lf.getLeaf(wf.getTypeCode());
        if (lf==null) {
            return new Result<>(false, "流程：" + wf.getTitle() + " 类型不存在！");
        }

        com.alibaba.fastjson.JSONObject jsonResult = new com.alibaba.fastjson.JSONObject();

        jsonResult.put("pageType", ConstUtil.PAGE_TYPE_FLOW_SHOW);
        jsonResult.put("flowId", flowId);
        jsonResult.put("myUserName", privilege.getUser(request));
        jsonResult.put("type", lf.getType());
        jsonResult.put("officeEditor", sysProperties.isWebOfficeEnabled() ? "web-office" : sysProperties.isOnlyOfficeEnabled() ? "onlyoffice" : "");
        if (sysProperties.isOnlyOfficeEnabled()) {
            jsonResult.put("onlyofficeServerUrl", sysProperties.getOnlyOfficeServerUrl());
        }

        Config cfg = Config.getInstance();
        // 0 表示抽屉 1 表示选项卡
        int moduleOpStyle = StrUtil.toInt(cfg.get("moduleOpStyle"), -1);
        if (moduleOpStyle == -1) {
            moduleOpStyle = ConstUtil.OP_STYLE_DRAWER;
        }
        jsonResult.put("isTabModule", moduleOpStyle == ConstUtil.OP_STYLE_TAB);

        MyActionDb mad = new MyActionDb();
        FormDb fd = new FormDb();
        String moduleCode = ParamUtil.get(request, "moduleCode");
        if (!StrUtil.isEmpty(moduleCode)) {
            ModuleSetupDb msd = new ModuleSetupDb();
            msd = msd.getModuleSetupDb(moduleCode);
            fd = fd.getFormDb(msd.getFormCode());
        } else {
            List<MyActionDb> myList = mad.getMyActionDbByUser(flowId, authUtil.getUserName());
            if (myList.size() > 0) {
                MyActionDb myFirstMad = myList.get(0);
                WorkflowActionDb myFirstWa = new WorkflowActionDb();
                myFirstWa = myFirstWa.getWorkflowActionDb((int) myFirstMad.getActionId());
                String actionForm = workflowService.getActionPropByFlowJson(wf, myFirstWa, "ActionForm");
                if (!StrUtil.isEmpty(actionForm)) {
                    fd = fd.getFormDb(actionForm);
                } else {
                    fd = fd.getFormDb(lf.getFormCode());
                }
            } else {
                fd = fd.getFormDb(lf.getFormCode());
            }
        }

        if (!fd.isLoaded()) {
            return new Result<>(false, i18nUtil.get("form") + lf.getFormCode() + i18nUtil.get("noLongerExist"));
        }

        IFormDAO fdao;
        com.redmoon.oa.flow.FormDAO dao = new com.redmoon.oa.flow.FormDAO();
        long moduleId = ParamUtil.getLong(request, "moduleId", -1);
        if (moduleId != -1 && !StrUtil.isEmpty(moduleCode)) {
            fdao = dao.getFormDAOById(moduleId, fd);
        } else {
            fdao = dao.getFormDAOByCache(flowId, fd);
        }

        if (fdao == null) {
            throw new ErrMsgException("流程表单中的数据已不存在");
        }

        jsonResult.put("formCode", fd.getCode());
        jsonResult.put("isLog", fd.isLog());

        ModuleSetupDb msd = new ModuleSetupDb();
        msd = msd.getModuleSetupDb(lf.getFormCode());
        String formJs = ModuleUtil.parseScript(flowId, fdao.getId(), lf.getFormCode(), null, -1, null, null, ConstUtil.PAGE_TYPE_LIST, msd.getScript("form_js"));
        jsonResult.put("formJs", StrUtil.getNullStr(formJs));

        com.redmoon.oa.person.UserMgr um = new com.redmoon.oa.person.UserMgr();

        String myUserName = privilege.getUser(request);
        String myRealName = um.getUserDb(myUserName).getRealName();

        // 是来自于收文界面paper_received_list.jsp，置公文为已读状态
        int paperId = ParamUtil.getInt(request, "paperId", -1);
        if (paperId!=-1) {
            PaperDistributeDb pdd = new PaperDistributeDb();
            pdd = pdd.getPaperDistributeDb(paperId);

            if (pdd.getInt("is_readed")==0) {
                pdd.set("is_readed", 1);
                pdd.set("read_date", new java.util.Date());
                try {
                    pdd.save();
                } catch (ResKeyException e) {
                    LogUtil.getLog(getClass()).error(e);
                }

                // 置日程为关闭状态
                PlanDb pd = new PlanDb();
                pd = pd.getPlanDb(myUserName, PlanDb.ACTION_TYPE_PAPER_DISTRIBUTE, String.valueOf(paperId));
                if (pd!=null) {
                    pd.setClosed(true);
                    try {
                        pd.save();
                    } catch (ErrMsgException e) {
                        LogUtil.getLog(getClass()).error(e);
                    }
                }
            }
        }

        // 置嵌套表需要用到的cwsId
        request.setAttribute("cwsId", String.valueOf(flowId));
        // 置嵌套表需要用到的curOperate
        request.setAttribute("pageType", ConstUtil.PAGE_TYPE_FLOW_SHOW);
        // 置NestSheetCtl需要用到的formCode
        request.setAttribute("formCode", fd.getCode());

        Directory dir = new Directory();
        Leaf ft = dir.getLeaf(wf.getTypeCode());
        boolean isFree = false;
        boolean isReactive = false;
        boolean isRecall = false;
        if (ft!=null) {
            isFree = ft.getType()!=Leaf.TYPE_LIST;
            WorkflowPredefineDb wfp = new WorkflowPredefineDb();
            wfp = wfp.getPredefineFlowOfFree(wf.getTypeCode());
            isReactive = wfp.isReactive();
            isRecall = wfp.isRecall();
        }

        jsonResult.put("isFlowManager", isFlowManager);

        boolean canUserView = lp.canUserQuery(privilege.getUser(request));
        if (!canUserView) {
            canUserView = privilege.isUserPrivValid(request, "paper.receive");
        }

        boolean isPaperReceived = false;
        if (!canUserView) {
            // 从抄送中访问
            String visitKey = ParamUtil.get(request, "visitKey");
            if (!"".equals(visitKey)) {
                String fId = String.valueOf(flowId);
                com.redmoon.oa.sso.Config ssoconfig = new com.redmoon.oa.sso.Config();
                String desKey = ssoconfig.get("key");
                visitKey = ThreeDesUtil.decrypthexstr(desKey, visitKey);
                if (visitKey.equals(fId)) {
                    canUserView = true;
                    // 当来自于智能模块时，moduleId不为-1
                    if (moduleId == -1) {
                        isPaperReceived = true;
                    }
                } else {
                    // 来自于模块的列表
                    int r = SecurityUtil.validateVisitKey(visitKey, fId);
                    if (r == 1) {
                        canUserView = true;
                        if (moduleId == -1) {
                            isPaperReceived = true;
                        }
                    }
                    else {
                        return new Result<>(false, SecurityUtil.getValidateVisitKeyErrMsg(r));
                    }
                }
            }
        }

        // 判断是否拥有查看流程过程的权限
        if (!isFlowManager && !canUserView) {
            // 判断是否参与了流程
            if (!wf.isUserAttended(privilege.getUser(request))) {
                return new Result<>(false, i18nUtil.get("pvg_invalid"));
            }
        }

        boolean isStarted = wf.isStarted();
        String op = ParamUtil.get(request, "op");
        String prompt = LocalUtil.LoadString(request,"res.flow.Flow","prompt");
        String canUserModifyFlow = cfg.get("canUserModifyFlow");
        String mode = "user";
        if ("true".equals(canUserModifyFlow)) {
            mode = "user";
        } else {
            mode = "view";
        }

        String flowExpireUnit = cfg.get("flowExpireUnit");
        boolean isHour = !"day".equals(flowExpireUnit);
        if ("day".equals(flowExpireUnit)){
            flowExpireUnit = LocalUtil.LoadString(request,"res.flow.Flow","day");
        }
        else{
            flowExpireUnit = LocalUtil.LoadString(request,"res.flow.Flow","hour");
        }

        boolean isNav = ParamUtil.getBoolean(request, "isNav", true);
        jsonResult.put("isNav", isNav);
        jsonResult.put("isHasAttachment", fd.isHasAttachment());

        // 如果是自由流程且是发起人
        boolean isFreeAndStarter = false;
        if (ft.getType()==Leaf.TYPE_FREE && myUserName.equals(wf.getUserName())) {
            isFreeAndStarter = true;
            WorkflowPredefineDb wfp = new WorkflowPredefineDb();
            wfp = wfp.getPredefineFlowOfFree(wf.getTypeCode());

            boolean isRoleMemberOfFlow = false;
            String[][] rolePrivs = wfp.getRolePrivsOfFree();
            for (String[] rolePriv : rolePrivs) {
                if (rolePriv[0].equals(RoleDb.CODE_MEMBER)) {
                    isRoleMemberOfFlow = true;
                    break;
                }
            }
            jsonResult.put("isRoleMemberOfFlow", isRoleMemberOfFlow);
        }

        jsonResult.put("isUseSMS", com.redmoon.oa.sms.SMSFactory.isUseSMS());

        /*Leaf lfParent = new Leaf();
        lfParent = lfParent.getLeaf(lf.getParentCode());*/

        int formViewId = ParamUtil.getInt(request, "formViewId", -1);
        FormViewDb formViewDb = new FormViewDb();
        Vector vView = formViewDb.getViews(lf.getFormCode());
        boolean hasView = false;
        if (vView.size()>0) {
            hasView = true;
            com.alibaba.fastjson.JSONArray aryView = new com.alibaba.fastjson.JSONArray();
            for (Object o : vView) {
                formViewDb = (FormViewDb) o;
                JSONObject json = new JSONObject();
                json.put("id", formViewDb.getLong("id"));
                json.put("name", formViewDb.getString("name"));
                aryView.add(json);
            }
            jsonResult.put("aryView", aryView);
        }
        jsonResult.put("hasView", hasView);

        com.redmoon.oa.flow.FlowConfig conf = new com.redmoon.oa.flow.FlowConfig();
        com.redmoon.oa.pvg.Privilege pvg = new com.redmoon.oa.pvg.Privilege();
        jsonResult.put("isZipBtnShow", pvg.isUserPrivValid(request, "flow.zip") && conf.getIsDisplay("FLOW_BUTTON_ZIP"));

        WorkflowPredefineDb wpd = new WorkflowPredefineDb();
        wpd = wpd.getPredefineFlowOfFree(wf.getTypeCode());

        // jsonResult.put("levelImg", WorkflowMgr.getLevelImg(request, wf));
        String flowTitle;
        if (wpd.isLight()) {
            flowTitle = MyActionMgr.renderTitle(request, wf);
        } else {
            flowTitle = wf.getTitle();
        }
        jsonResult.put("flowTitle", flowTitle);

        boolean hasQuery = false;
        if (lf.getQueryId()!=Leaf.QUERY_NONE) {
            // 判断权限，管理员能看见查询，其它人员根据角色进行判断
            String[] roles = StrUtil.split(lf.getQueryRole(), ",");
            boolean canSeeQuery = false;
            if (!privilege.isUserPrivValid(request, "admin")) {
                if (roles != null) {
                    UserDb user = new UserDb();
                    user = user.getUserDb(privilege.getUser(request));
                    for (String role : roles) {
                        if (user.isUserOfRole(role)) {
                            canSeeQuery = true;
                            break;
                        }
                    }
                } else {
                    canSeeQuery = true;
                }
            } else {
                canSeeQuery = true;
            }
            FormQueryDb aqd = new FormQueryDb();
            aqd = aqd.getFormQueryDb((int) lf.getQueryId());
            if (canSeeQuery && aqd.isLoaded()) {
                hasQuery = true;

                String colratio = "";
                String colP = aqd.getColProps();
                if (colP == null || "".equals(colP)) {
                    colP = "[]";
                }
                int tableWidth = 0;
                com.alibaba.fastjson.JSONArray jsonArray = com.alibaba.fastjson.JSONArray.parseArray(colP);
                for (int i=0; i<jsonArray.size(); i++) {
                    com.alibaba.fastjson.JSONObject json = jsonArray.getJSONObject(i);
                    if (json.getBoolean("hide")) {
                        continue;
                    }
                    String name = (String)json.get("name");
                    if ("cws_op".equalsIgnoreCase(name)) {
                        continue;
                    }
                    tableWidth += json.getIntValue("width");
                    if ("".equals(colratio)) {
                        colratio = "" + json.get("width");
                    } else {
                        colratio += "," + json.get("width");
                    }
                }

                String queryAjaxUrl;
                if (aqd.isScript()) {
                    queryAjaxUrl = "flow/formQueryListScriptEmbed";
                }
                else {
                    queryAjaxUrl = "flow/formQueryListEmbed";
                }

                jsonResult.put("colRatio", colratio);
                jsonResult.put("queryName", aqd.getQueryName());
                jsonResult.put("queryAjaxUrl", queryAjaxUrl);
                jsonResult.put("queryId", lf.getQueryId());
                jsonResult.put("queryTableWidth", tableWidth + 2);

                com.alibaba.fastjson.JSONArray aryCond = new com.alibaba.fastjson.JSONArray();
                com.alibaba.fastjson.JSONObject condMap = com.alibaba.fastjson.JSONObject.parseObject(lf.getQueryCondMap());
                Set keys = condMap.keySet();
                for (Object o : keys) {
                    String qField = (String)o;
                    com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
                    json.put("fieldName", qField);
                    json.put("fieldValue", fdao.getFieldValue(qField));
                    aryCond.add(json);
                }
                jsonResult.put("aryCond", aryCond);
            }
        }
        jsonResult.put("hasQuery", hasQuery);
        jsonResult.put("flowTypeCode", lf.getCode());

        int doc_id = wf.getDocId();
        DocumentMgr dm = new DocumentMgr();
        Document doc = dm.getDocument(doc_id);

        String starter = um.getUserDb(wf.getUserName()).getRealName();
        String flowStatusDesc = wf.getStatusDesc();
        jsonResult.put("starter", starter);
        jsonResult.put("starterUserName", wf.getUserName());
        jsonResult.put("flowStatus", wf.getStatus());
        jsonResult.put("flowStatusDesc", flowStatusDesc);

        boolean isShowDelInfo = false;
        if (isFlowManager &&  wf.getStatus()==WorkflowDb.STATUS_DELETED) {
            if (!"".equals(StrUtil.getNullStr(wf.getDelUser()))) {
                isShowDelInfo = true;
                jsonResult.put("delByUser", um.getUserDb(wf.getDelUser()).getRealName());
                jsonResult.put("delTime", DateUtil.format(wf.getDelDate(), "yyyy-MM-dd HH:mm:ss"));
            }
        }
        jsonResult.put("isShowDelInfo", isShowDelInfo);

        String content;
        Render rd = new Render(request, wf, doc);
        if (formViewId!=-1) {
            content = rd.reportForView(formViewId, true);
        }
        else {
            content = rd.report();
        }
        jsonResult.put("isRendWithoutViewRule", fd.isRendWithoutViewRule());
        // 如果不分离渲染
        if (!fd.isRendWithoutViewRule()) {
            content += flowRender.reportScript(fd, fdao);
        }

        jsonResult.put("formViewId", formViewId);
        jsonResult.put("content", content);
        jsonResult.put("fdaoId", fdao.getId());

        boolean canDelAtt = false;
        if (privilege.isUserPrivValid(request, "admin") || isFlowManager) {
            canDelAtt = true;
        }
        jsonResult.put("canDelAtt", canDelAtt);
        jsonResult.put("docId", doc_id);

        // 回复中的附件
        /*WorkflowAnnexAttachment wfaa = new WorkflowAnnexAttachment();
        java.util.Vector<WorkflowAnnexAttachment> annexAtt = wfaa.getAllAttachments(flowId);
        com.alibaba.fastjson.JSONArray aryAnnexAtt = new com.alibaba.fastjson.JSONArray();
        for (WorkflowAnnexAttachment wfaatt : annexAtt) {
            long annexId = wfaatt.getAnnexId();
            WorkflowAnnexDb wfadb = new WorkflowAnnexDb();
            wfadb = (WorkflowAnnexDb) wfadb.getQObjectDb(annexId);
            String annexUser = wfadb.getString("user_name");
            UserDb annexUserDb = new UserDb(annexUser);

            com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
            json.put("url", wfaatt.getAttachmentUrl(request));
            json.put("name", wfaatt.getName());
            json.put("realName", annexUserDb.getRealName());
            json.put("addDate", wfadb.getDate("add_date"));
            json.put("size", NumberUtil.round((double) wfaatt.getSize() / 1024000, 2));

            aryAnnexAtt.add(json);
        }
        jsonResult.put("aryAnnexAtt", aryAnnexAtt);*/

        jsonResult.put("flowPerformanceDisplay", cfg.getBooleanProperty("flowPerformanceDisplay"));
        jsonResult.put("isReactive", wpd.isReactive());
        jsonResult.put("flowIsRemarkShow", cfg.getBooleanProperty("flowIsRemarkShow"));
        // jsonResult.put("isOpinionRelateWithRemark", cfg.getBooleanProperty("isOpinionRelateWithRemark"));
        jsonResult.put("flowExpireUnit", flowExpireUnit);
        jsonResult.put("isRemarkShowInProcessList", cfg.getBoolean("isRemarkShowInProcessList"));

        jsonResult.put("CHECK_STATUS_NOT", MyActionDb.CHECK_STATUS_NOT);
        jsonResult.put("CHECK_STATUS_CHECKED", MyActionDb.CHECK_STATUS_CHECKED);
        jsonResult.put("CHECK_STATUS_WAITING_TO_DO", MyActionDb.CHECK_STATUS_WAITING_TO_DO);
        jsonResult.put("CHECK_STATUS_WAITING_TO_DO_IN_ACTION", MyActionDb.CHECK_STATUS_WAITING_TO_DO_IN_ACTION);
        jsonResult.put("CHECK_STATUS_PASS", MyActionDb.CHECK_STATUS_PASS);
        jsonResult.put("CHECK_STATUS_PASS_BY_RETURN", MyActionDb.CHECK_STATUS_PASS_BY_RETURN);
        jsonResult.put("CHECK_STATUS_SUSPEND", MyActionDb.CHECK_STATUS_SUSPEND);
        jsonResult.put("CHECK_STATUS_RETURN", MyActionDb.CHECK_STATUS_RETURN);

        com.alibaba.fastjson.JSONArray aryMyAction = workflowService.listProcessForShow(wf, myUserName, isPaperReceived, isRecall, isFlowManager, isReactive);
        jsonResult.put("aryMyAction", aryMyAction);

        boolean isLight = false;
        if (isFree) {
            if ((myUserName.equals(mad.getUserName()) || myUserName.equals(mad.getProxyUserName())) && !mad.isChecked()) {
                if (wpd.isLight()) {
                    isLight = true;
                }
            }
        }
        jsonResult.put("isFree", isFree);
        jsonResult.put("isLight", isLight);

        boolean isDelayed = false;
        WorkflowActionDb actionDelayed = new WorkflowActionDb();
        Vector<WorkflowActionDb> vdelayed = actionDelayed.getActionsDelayedOfFlow(flowId);
        Iterator<WorkflowActionDb> irdelayed = vdelayed.iterator();
        if (vdelayed.size()>0) {
            isDelayed = true;
            com.alibaba.fastjson.JSONArray aryDelayed = new com.alibaba.fastjson.JSONArray();
            while (irdelayed.hasNext()) {
                com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
                actionDelayed = irdelayed.next();
                json.put("title", actionDelayed.getTitle());
                json.put("jobName", actionDelayed.getJobName());
                json.put("dateDelayed", DateUtil.format(actionDelayed.getDateDelayed(), "yyyy-MM-dd HH:mm"));
                aryDelayed.add(json);
            }
            jsonResult.put("aryDelayed", aryDelayed);
        }
        jsonResult.put("isDelayed", isDelayed);
        jsonResult.put("flowRemark", wf.getRemark());

        jsonResult.put("flowIsBtnExportWordShow", cfg.getBooleanProperty("flowIsBtnExportWordShow"));

        jsonResult.put("isFlowStarted", wf.isStarted());
        jsonResult.put("isReply", wpd.isReply());
        jsonResult.put("isProgress", fd.isProgress());

        com.alibaba.fastjson.JSONArray aryAnnex = new com.alibaba.fastjson.JSONArray();
        WorkflowAnnexDb wad = new WorkflowAnnexDb();
        Vector<WorkflowAnnexDb> vec1 = wad.listRoot(wf.getId(), myUserName);
        for (WorkflowAnnexDb workflowAnnexDb : vec1) {
            wad = workflowAnnexDb;

            com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
            json.put("realName", um.getUserDb(wad.getString("user_name")).getRealName());
            json.put("process", wad.getInt("progress"));
            json.put("content", wad.getString("content"));
            json.put("id", wad.getLong("id"));
            json.put("addDate", wad.getDate("add_date"));
            json.put("flowId", wad.getString("flow_id"));
            json.put("actionId", wad.getString("action_id"));
            json.put("userName", wad.getString("user_name"));
            aryAnnex.add(json);

            WorkflowAnnexDb wad2 = new WorkflowAnnexDb();
            com.alibaba.fastjson.JSONArray aryAnnexSub = new com.alibaba.fastjson.JSONArray();
            Vector<WorkflowAnnexDb> vec2 = wad2.listChildren(wad.getInt("id"), myUserName);
            for (WorkflowAnnexDb annexDb : vec2) {
                wad2 = annexDb;
                int id2 = (int) wad2.getLong("id");

                com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject();
                jsonObject.put("id", id2);
                jsonObject.put("userName", wad2.getString("user_name"));
                jsonObject.put("realName", um.getUserDb(wad2.getString("user_name")).getRealName());
                jsonObject.put("content", wad2.getString("content"));
                jsonObject.put("addDate", wad2.getDate("add_date"));
                jsonObject.put("flowId", wad2.getString("flow_id"));
                jsonObject.put("actionId", wad2.getString("action_id"));
                jsonObject.put("replyRealName", um.getUserDb(wad2.getString("reply_name")).getRealName());
                aryAnnexSub.add(jsonObject);
            }
            json.put("aryAnnexSub", aryAnnexSub);
        }
        jsonResult.put("aryAnnex", aryAnnex);

        boolean isPageStyleLight = false;
        int cfgPageStyle = cfg.getInt("pageStyle");
        if (cfgPageStyle == ConstUtil.PAGE_STYLE_LIGHT || (cfgPageStyle == ConstUtil.PAGE_STYLE_BY_FORM_SETUP && fd.getPageStyle() == ConstUtil.PAGE_STYLE_LIGHT)) {
            isPageStyleLight = true;
        }
        jsonResult.put("isPageStyleLight", isPageStyleLight);

        WorkflowRuler wrTop = new WorkflowRuler();
        WorkflowMgr wfmTop = new WorkflowMgr();
        WorkflowDb wfTop = wfmTop.getWorkflowDb(flowId);

        com.redmoon.oa.flow.Directory dirTop = new com.redmoon.oa.flow.Directory();
        Leaf ftTop = dirTop.getLeaf(wfTop.getTypeCode());

        boolean canUserSeeFlowChartTop = cfg.getBooleanProperty("canUserSeeFlowChart");
        boolean isFlowManagerTop = false;
        if (privilege.isUserPrivValid(request, "admin")) {
            isFlowManagerTop = true;
        } else {
            LeafPriv lpTop = new LeafPriv(wfTop.getTypeCode());
            if (privilege.isUserPrivValid(request, "admin.flow")) {
                if (lpTop.canUserExamine(privilege.getUser(request))) {
                    isFlowManagerTop = true;
                }
            }
        }
        String visitKeyTop = ParamUtil.get(request, "visitKey");
        jsonResult.put("canManageFlow", isFlowManagerTop);
        jsonResult.put("visitKey", visitKeyTop);

        boolean isFlowChartShow = false;
        if (canUserSeeFlowChartTop || isFlowManagerTop) {
            isFlowChartShow = true;
        }
        jsonResult.put("isFlowChartShow", isFlowChartShow);

        boolean canModifyTitle = false;
        if (privilege.isUserPrivValid(request, "admin") || isFlowManagerTop || wrTop.canUserModifyFlow(request, wfTop)) {
            canModifyTitle = true;
        }
        jsonResult.put("canModifyTitle", canModifyTitle);

        boolean isNotifyTabShow = false;
        PaperDistributeDb pddTop = new PaperDistributeDb();
        int paperCountTop = pddTop.getCountOfWorkflow(flowId);
        if (paperCountTop > 0) {
            isNotifyTabShow = true;
            String notifyTabName = i18nUtil.get("notify"); // "流程抄送";
            String kindTop = com.redmoon.oa.kernel.License.getInstance().getKind();
            if (kindTop.equalsIgnoreCase(com.redmoon.oa.kernel.License.KIND_COM)) {
                notifyTabName = "流程知会";
            }
            jsonResult.put("notifyTabName", notifyTabName);
        }
        jsonResult.put("isNotifyTabShow", isNotifyTabShow);

        // 如果配置了自动存档目录
        boolean isArchiveTabShow = false;
        WorkflowPredefineDb wpdTop = new WorkflowPredefineDb();
        wpdTop = wpdTop.getDefaultPredefineFlow(wfTop.getTypeCode());
        if (!"".equals(wpdTop.getDirCode())) {
            isArchiveTabShow = true;
        }
        jsonResult.put("isArchiveTabShow", isArchiveTabShow);

        boolean isDiscardBtnShow = false;
        // 如果用户处理的节点上设置了可以放弃
        if (wf.getStatus() == WorkflowDb.STATUS_NOT_STARTED || wf.getStatus() == WorkflowDb.STATUS_STARTED) {
            if (conf.getIsDisplay("FLOW_BUTTON_DISCARD")) {
                // 流程被干预以后，可能会删除开始节点
                MyActionDb firstMad = mad.getFirstMyActionDbOfFlow(flowId);
                WorkflowActionDb firstWa = new WorkflowActionDb();
                firstWa = firstWa.getWorkflowActionDb((int)firstMad.getActionId());
                if (firstWa.canDiscard()) {
                    isDiscardBtnShow = true;
                }
            }
        }
        jsonResult.put("isDiscardBtnShow", isDiscardBtnShow);

        // 发起人或者流程管理员可以变更
        LeafPriv leafPriv = new LeafPriv(wf.getTypeCode());
        boolean isBtnAlterShow = false;
        if (isReactive && (wf.getStatus() == WorkflowDb.STATUS_FINISHED || wf.getStatus() == WorkflowDb.STATUS_DISCARDED) && (wf.getUserName().equals(authUtil.getUserName()) || leafPriv.canUserExamine(authUtil.getUserName()))) {
            isBtnAlterShow = true;
            jsonResult.put("btnAlterName", conf.getBtnName("FLOW_BUTTON_ALTER").startsWith("#") ? i18nUtil.get("alter") : conf.getBtnName("FLOW_BUTTON_ALTER"));
        }
        jsonResult.put("isBtnAlterShow", isBtnAlterShow);

        boolean isBtnForceFinishShow = false;
        if (conf.getIsDisplay("FLOW_BUTTON_FORCE_FINISH")) {
            if (wf.getStatus() == WorkflowDb.STATUS_NOT_STARTED || wf.getStatus() == WorkflowDb.STATUS_STARTED) {
                if (leafPriv.canUserExamine(authUtil.getUserName())) {
                    isBtnForceFinishShow = true;
                    jsonResult.put("btnForceFinishName", conf.getBtnName("FLOW_BUTTON_FORCE_FINISH").startsWith("#") ? i18nUtil.get("btnForceFinish") : conf.getBtnName("FLOW_BUTTON_FORCE_FINISH"));
                }
            }
        }
        jsonResult.put("isBtnForceFinishShow", isBtnForceFinishShow);

        boolean isBtnRollBackShow = false;
        if (conf.getIsDisplay("FLOW_BUTTON_ROLL_BACK")) {
            if (licenseUtil.isEnabled(LicenseConstUtil.FLOW_ROLLBACK) && leafPriv.canUserExamine(authUtil.getUserName())) {
                isBtnRollBackShow = true;
                jsonResult.put("btnRollBackName", conf.getBtnName("FLOW_BUTTON_ROLL_BACK").startsWith("#") ? i18nUtil.get("btnRollBack") : conf.getBtnName("FLOW_BUTTON_ROLL_BACK"));
            }
        }
        jsonResult.put("isBtnRollBackShow", isBtnRollBackShow);

        boolean isBtnForceRecallShow = false;
        if (wf.getStatus() == WorkflowDb.STATUS_STARTED && conf.getIsDisplay("FLOW_BUTTON_FORCE_RECALL")) {
            List<MyActionDb> list = mad.getMyActionDbCheckedByUser(flowId, authUtil.getUserName());
            WorkflowActionDb wa = new WorkflowActionDb();
            for (MyActionDb madChecked : list) {
                wa = wa.getWorkflowActionDb((int)madChecked.getActionId());
                if (wa.canForceRecall() && wa.getStatus()!= WorkflowActionDb.STATE_DOING) {
                    isBtnForceRecallShow = true;
                    jsonResult.put("btnForceRecallName", conf.getBtnName("FLOW_BUTTON_FORCE_RECALL").startsWith("#") ? i18nUtil.get("btnForceRecall") : conf.getBtnName("FLOW_BUTTON_FORCE_RECALL"));
                    jsonResult.put("actionIdForceRecall", wa.getId());
                    break;
                }
            }
        }
        jsonResult.put("isBtnForceRecallShow", isBtnForceRecallShow);

        boolean isBtnAppendUserShow = false;
        if (/*wf.getStatus() == WorkflowDb.STATUS_STARTED && */conf.getIsDisplay("FLOW_BUTTON_APPEND_USER")) {
            List<MyActionDb> list = mad.getMyActionDbCheckedByUser(flowId, authUtil.getUserName());
            WorkflowActionDb wa = new WorkflowActionDb();
            for (MyActionDb madChecked : list) {
                wa = wa.getWorkflowActionDb((int)madChecked.getActionId());
                if (wa.canAppendUser()) {
                    isBtnAppendUserShow = true;
                    jsonResult.put("btnAppendUserName", conf.getBtnName("FLOW_BUTTON_APPEND_USER").startsWith("#") ? i18nUtil.get("btnAppendUser") : conf.getBtnName("FLOW_BUTTON_APPEND_USER"));
                    jsonResult.put("myActionIdAppendUser", madChecked.getId());
                    break;
                }
            }
        }
        jsonResult.put("isBtnAppendUserShow", isBtnAppendUserShow);

        // 处理过程的表头
        WorkflowConfig workflowConfig = WorkflowConfig.getInstance();
        if (isFlowManager) {
            jsonResult.put("myActionListCols", workflowConfig.getProperty("flowMyActionListColsForManager"));
        } else {
            jsonResult.put("myActionListCols", workflowConfig.getProperty("flowMyActionListColsForNormal"));
        }
        jsonResult.put("skey", new Privilege().getSkey(authUtil.getUserName()));
        return new Result<>(jsonResult);
    }

    @ApiOperation(value = "取办理记录后面的节点", notes = "取办理记录后面的节点，用于追加用户", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "myActionId", value = "待办记录ID", required = true, dataType = "Long"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/getLinkToActions", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<com.alibaba.fastjson.JSONArray> getLinkToActions(@RequestParam(value = "myActionId") Long myActionId) {
        JSONArray ary = new JSONArray();
        MyActionDb mad = new MyActionDb();
        mad = mad.getMyActionDb(myActionId);
        if (!mad.isLoaded()) {
            return new Result<>(false, "待办记录不存在");
        }
        if (!mad.getUserName().equals(authUtil.getUserName())) {
            return new Result<>(false, "非法操作");
        }
        WorkflowActionDb wad = new WorkflowActionDb();
        wad = wad.getWorkflowActionDb((int)mad.getActionId());

        for (WorkflowActionDb wa : wad.getLinkToActions()) {
            JSONObject json = new JSONObject();
            json.put("id", wa.getId());
            json.put("internalName", wa.getInternalName());
            json.put("title", wa.getTitle());
            json.put("userName", wa.getUserName());
            json.put("userRealName", wa.getUserRealName());
            json.put("status", WorkflowActionDb.getStatusName(wa.getStatus()));
            json.put("jobName", wa.getJobName());
            ary.add(json);
        }
        return new Result<>(ary);
    }

    @ApiOperation(value = "获取显示规则脚本", notes = "获取显示规则脚本", httpMethod = "POST")
    @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flowShowScript", method={RequestMethod.GET, RequestMethod.POST})
    public Result<com.alibaba.fastjson.JSONObject> flowShowScript(@RequestParam(value="flowId") Integer flowId) {
        WorkflowMgr wfm = new WorkflowMgr();
        WorkflowDb wf = wfm.getWorkflowDb(flowId);

        if (!wf.isLoaded()) {
            return new Result<>(false, i18nUtil.get("processNotExist"));
        }

        request.setAttribute("pageType", ConstUtil.PAGE_TYPE_FLOW_SHOW);

        Leaf lf = new Leaf();
        lf = lf.getLeaf(wf.getTypeCode());

        FormDb fd = new FormDb();
        String moduleCode = ParamUtil.get(request, "moduleCode");
        if (!StrUtil.isEmpty(moduleCode)) {
            ModuleSetupDb msd = new ModuleSetupDb();
            msd = msd.getModuleSetupDb(moduleCode);
            fd = fd.getFormDb(msd.getFormCode());
        } else {
            MyActionDb mad = new MyActionDb();
            List<MyActionDb> myList = mad.getMyActionDbByUser(flowId, authUtil.getUserName());
            if (myList.size() > 0) {
                MyActionDb myFirstMad = myList.get(0);
                WorkflowActionDb myFirstWa = new WorkflowActionDb();
                myFirstWa = myFirstWa.getWorkflowActionDb((int) myFirstMad.getActionId());
                String actionForm = workflowService.getActionPropByFlowJson(wf, myFirstWa, "ActionForm");
                if (!StrUtil.isEmpty(actionForm)) {
                    fd = fd.getFormDb(actionForm);
                } else {
                    fd = fd.getFormDb(lf.getFormCode());
                }
            } else {
                fd = fd.getFormDb(lf.getFormCode());
            }
        }

        if (!fd.isLoaded()) {
            return new Result<>(false, i18nUtil.get("form") + lf.getFormCode() + i18nUtil.get("noLongerExist"));
        }

        IFormDAO fdao;
        com.redmoon.oa.flow.FormDAO dao = new com.redmoon.oa.flow.FormDAO();
        long moduleId = ParamUtil.getLong(request, "moduleId", -1);
        if (moduleId != -1 && !StrUtil.isEmpty(moduleCode)) {
            fdao = dao.getFormDAOById(moduleId, fd);
        } else {
            fdao = dao.getFormDAOByCache(flowId, fd);
        }

        if (fdao == null) {
            throw new ErrMsgException("流程表单中的数据已不存在");
        }

        JSONObject json = new JSONObject();
        json.put("script", flowRender.reportScript(fd, fdao));
        return new Result<>(json);
    }

    @ApiOperation(value = "查看流程视图", notes = "查看流程视图", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "viewId", value = "视图ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flowView", method={RequestMethod.GET, RequestMethod.POST})
    public Result<com.alibaba.fastjson.JSONObject> flowView(@RequestParam(value="flowId") Integer flowId, @RequestParam(value="viewId")Integer viewId) {
        WorkflowMgr wfm = new WorkflowMgr();
        WorkflowDb wf = wfm.getWorkflowDb(flowId);

        if (!wf.isLoaded()) {
            return new Result<>(false, i18nUtil.get("processNotExist"));
        }

        // 检查
        Leaf lf = new Leaf();
        lf = lf.getLeaf(wf.getTypeCode());
        if (lf==null) {
            return new Result<>(false, "流程：" + wf.getTitle() + " 类型不存在！");
        }
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        com.alibaba.fastjson.JSONObject jsonResult = new com.alibaba.fastjson.JSONObject();

        jsonResult.put("flowId", flowId);
        jsonResult.put("myUserName", privilege.getUser(request));

        FormDb fd = new FormDb();
        fd = fd.getFormDb(lf.getFormCode());
        if (!fd.isLoaded()) {
            return new Result<>(false, i18nUtil.get("form") + lf.getFormCode() + i18nUtil.get("noLongerExist"));
        }

        boolean isPageStyleLight = false;
        ModuleSetupDb msd = new ModuleSetupDb();
        msd = msd.getModuleSetupDb(lf.getFormCode());
        Config cfg = Config.getInstance();
        int cfgPageStyle = cfg.getInt("pageStyle");
        if (cfgPageStyle == ConstUtil.PAGE_STYLE_LIGHT || (cfgPageStyle == ConstUtil.PAGE_STYLE_BY_FORM_SETUP && fd.getPageStyle() == ConstUtil.PAGE_STYLE_LIGHT)) {
            isPageStyleLight = true;
        }
        jsonResult.put("isPageStyleLight", isPageStyleLight);

        com.redmoon.oa.flow.FormDAO fdao = new com.redmoon.oa.flow.FormDAO();
        fdao = fdao.getFormDAOByCache(flowId, fd);

        String myUserName = privilege.getUser(request);
        Directory dir = new Directory();
        Leaf ft = dir.getLeaf(wf.getTypeCode());
        boolean isFree = false;
        boolean isReactive = false;
        boolean isRecall = false;
        if (ft!=null) {
            isFree = ft.getType()!=Leaf.TYPE_LIST;
            WorkflowPredefineDb wfp = new WorkflowPredefineDb();
            wfp = wfp.getPredefineFlowOfFree(wf.getTypeCode());
            isReactive = wfp.isReactive();
            isRecall = wfp.isRecall();
        }

        boolean isFlowManager = false;
        LeafPriv lp = new LeafPriv(wf.getTypeCode());
        if (privilege.isUserPrivValid(request, "admin.flow")) {
            if (lp.canUserExamine(privilege.getUser(request))) {
                isFlowManager = true;
            }
        }

        boolean canUserView = lp.canUserQuery(privilege.getUser(request));

        if (!canUserView) {
            canUserView = privilege.isUserPrivValid(request, "paper.receive");
        }

        boolean isPaperReceived = false;
        if (!canUserView) {
            // 从流程控件或知会中访问
            String visitKey = ParamUtil.get(request, "visitKey");
            if (!"".equals(visitKey)) {
                String fId = String.valueOf(flowId);
                com.redmoon.oa.sso.Config ssoconfig = new com.redmoon.oa.sso.Config();
                String desKey = ssoconfig.get("key");
                visitKey = ThreeDesUtil.decrypthexstr(desKey, visitKey);
                if (visitKey.equals(fId)) {
                    canUserView = true;
                    isPaperReceived = true;
                }
            }
        }

        // 判断是否拥有查看流程过程的权限
        if (!isFlowManager && !canUserView) {
            // 判断是否参与了流程
            if (!wf.isUserAttended(privilege.getUser(request))) {
                return new Result<>(false, i18nUtil.get("pvg_invalid"));
            }
        }

        WorkflowPredefineDb wpd = new WorkflowPredefineDb();
        wpd = wpd.getPredefineFlowOfFree(wf.getTypeCode());

        // jsonResult.put("levelImg", WorkflowMgr.getLevelImg(request, wf));
        String flowTitle;
        if (wpd.isLight()) {
            flowTitle = MyActionMgr.renderTitle(request, wf);
        } else {
            flowTitle = wf.getTitle();
        }
        jsonResult.put("flowTitle", flowTitle);

        int doc_id = wf.getDocId();
        DocumentMgr dm = new DocumentMgr();
        Document doc = dm.getDocument(doc_id);

        Render rd = new Render(request, wf, doc);
        String content = rd.reportForView(viewId, true);

        jsonResult.put("content", content);
        jsonResult.put("fdaoId", fdao.getId());
        jsonResult.put("flowIsRemarkShow", cfg.getBooleanProperty("flowIsRemarkShow"));

        com.alibaba.fastjson.JSONArray aryMyAction = workflowService.listProcessForShow(wf, myUserName, isPaperReceived, isRecall, isFlowManager, isReactive);
        jsonResult.put("aryMyAction", aryMyAction);
        return new Result<>(jsonResult);
    }

    @ApiOperation(value = "修改流程标题", notes = "修改流程标题", httpMethod = "POST")
    @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flowModifyTitle", method={RequestMethod.GET})
    public Result<Object> flowModifyTitle(@RequestParam(value="flowId") Integer flowId) {
        WorkflowMgr wfm = new WorkflowMgr();
        WorkflowDb wf = wfm.getWorkflowDb(flowId);

        boolean isFlowManager = false;
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        LeafPriv lp = new LeafPriv(wf.getTypeCode());
        if (privilege.isUserPrivValid(request, "admin.flow")) {
            if (lp.canUserExamine(privilege.getUser(request))) {
                isFlowManager = true;
            }
        }

        boolean canModifyTitle = false;
        WorkflowRuler wr = new WorkflowRuler();
        if (privilege.isUserPrivValid(request, "admin") || isFlowManager || wr.canUserModifyFlow(request, wf)) {
            canModifyTitle = true;
        }
        else {
            return new Result<>(false, i18nUtil.get("pvg_invalid"));
        }

        com.alibaba.fastjson.JSONObject jsonResult = new com.alibaba.fastjson.JSONObject();
        jsonResult.put("canModifyTitle", canModifyTitle);

        // jsonResult.put("skinPath", SkinMgr.getSkinPath(request, false));
        jsonResult.put("myUserName", privilege.getUser(request));
        jsonResult.put("canManageFlow", privilege.isUserPrivValid(request, "admin") || isFlowManager);

        String visitKeyTop = ParamUtil.get(request, "visitKey");
        jsonResult.put("visitKey", visitKeyTop);

        boolean isFlowChartShow = false;
        boolean isFree = false;
        com.redmoon.oa.flow.Directory dirTop = new com.redmoon.oa.flow.Directory();
        Leaf lf = dirTop.getLeaf(wf.getTypeCode());
        isFree = lf.getType() != Leaf.TYPE_LIST;

        Config cfg = Config.getInstance();
        boolean canUserSeeFlowChart = cfg.getBooleanProperty("canUserSeeFlowChart");
        boolean canUserSeeFlowImage = cfg.getBooleanProperty("canUserSeeFlowImage");
        boolean canUserSeeDesignerWhenDispose = cfg.getBooleanProperty("canUserSeeDesignerWhenDispose");
        if (!isFree && (canUserSeeFlowChart || canUserSeeFlowImage || canUserSeeDesignerWhenDispose || isFlowManager)) {
            isFlowChartShow = true;
        }
        jsonResult.put("isFlowChartShow", isFlowChartShow);

        jsonResult.put("flowTypeName", lf.getName());
        jsonResult.put("flowTypeCode", lf.getCode());

        jsonResult.put("isFlowLevelDisplay", cfg.getBooleanProperty("isFlowLevelDisplay"));
        jsonResult.put("level", wf.getLevel());
        jsonResult.put("LEVEL_NORMAL", WorkflowDb.LEVEL_NORMAL);
        jsonResult.put("LEVEL_IMPORTANT", WorkflowDb.LEVEL_IMPORTANT);
        jsonResult.put("LEVEL_URGENT", WorkflowDb.LEVEL_URGENT);

        jsonResult.put("flowId", wf.getId());
        jsonResult.put("flowTitle", wf.getTitle());
        return new Result<>(jsonResult);
    }

    @ApiOperation(value = "查看流程图", notes = "查看流程图", httpMethod = "GET")
    @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flowShowChart", method={RequestMethod.GET})
    public Result<com.alibaba.fastjson.JSONObject> flowShowChart(@RequestParam(value = "flowId") Integer flowId) {
        WorkflowMgr wfm = new WorkflowMgr();
        WorkflowDb wf = wfm.getWorkflowDb(flowId);
        com.redmoon.oa.Config cfg = Config.getInstance();
        String flowExpireUnit = cfg.get("flowExpireUnit");
        boolean isHour = !"day".equals(flowExpireUnit);
        if ("day".equals(flowExpireUnit)) {
            flowExpireUnit = LocalUtil.LoadString(request, "res.flow.Flow", "day");
        } else {
            flowExpireUnit = LocalUtil.LoadString(request, "res.flow.Flow", "hour");
        }

        com.alibaba.fastjson.JSONObject jsonResult = new com.alibaba.fastjson.JSONObject();
        jsonResult.put("flowExpireUnit", flowExpireUnit);
        jsonResult.put("licenseKey", License.getInstance().getKey());

        boolean isFlowManager = false;
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        LeafPriv lp = new LeafPriv(wf.getTypeCode());
        if (privilege.isUserPrivValid(request, "admin.flow")) {
            if (lp.canUserExamine(privilege.getUser(request))) {
                isFlowManager = true;
            }
        }

        boolean isFlowDebug = ParamUtil.getBoolean(request, "isFlowDebug", false);
        boolean canUserSeeFlowChart = cfg.getBooleanProperty("canUserSeeFlowChart");

        jsonResult.put("canUserSeeFlowChart", canUserSeeFlowChart || isFlowManager || isFlowDebug);
        if (canUserSeeFlowChart || isFlowManager || isFlowDebug) {
            String flowJson = "";
            if (StringUtils.isEmpty(wf.getFlowJson())) {
                IMyflowUtil myflowUtil = SpringUtil.getBean(IMyflowUtil.class);
                try {
                    flowJson = myflowUtil.toMyflow(wf.getFlowString());
                } catch (ErrMsgException e) {
                    LogUtil.getLog(getClass()).error(e);
                }
            } else {
                flowJson = wf.getFlowJson();
            }
            jsonResult.put("flowJson", flowJson);
        }

        jsonResult.put("flowId", flowId);
        jsonResult.put("myUserName", privilege.getUser(request));
        jsonResult.put("canManageFlow", privilege.isUserPrivValid(request, "admin") || isFlowManager);

        String visitKeyTop = ParamUtil.get(request, "visitKey");
        jsonResult.put("visitKey", visitKeyTop);
        boolean isFlowChartShow = false;
        com.redmoon.oa.flow.Directory dirTop = new com.redmoon.oa.flow.Directory();
        Leaf ftTop = dirTop.getLeaf(wf.getTypeCode());
        boolean isFreeTop = false;
        if (ftTop != null) {
            isFreeTop = ftTop.getType() != Leaf.TYPE_LIST;
        }
        boolean canUserSeeFlowChartTop = cfg.getBooleanProperty("canUserSeeFlowChart");
        if (canUserSeeFlowChartTop || isFlowManager) {
            isFlowChartShow = true;
        }
        jsonResult.put("isFlowChartShow", isFlowChartShow);
        Leaf lf = new Leaf();
        lf = lf.getLeaf(wf.getTypeCode());
        jsonResult.put("isDebug", lf.isDebug());

        // 取出激活节点和已办节点
        com.alibaba.fastjson.JSONArray activeActions = new com.alibaba.fastjson.JSONArray();
        com.alibaba.fastjson.JSONArray finishActions = new com.alibaba.fastjson.JSONArray();
        com.alibaba.fastjson.JSONArray ignoreActions = new com.alibaba.fastjson.JSONArray();
        com.alibaba.fastjson.JSONArray discardActions = new com.alibaba.fastjson.JSONArray();
        com.alibaba.fastjson.JSONArray returnActions = new com.alibaba.fastjson.JSONArray();

        for (WorkflowActionDb wa : wf.getActions()) {
            if (wa.getStatus() == WorkflowActionDb.STATE_DOING) {
                com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
                json.put("ID", wa.getInternalName());
                activeActions.add(json);
            } else if (wa.getStatus() == WorkflowActionDb.STATE_FINISHED) {
                com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
                json.put("ID", wa.getInternalName());
                finishActions.add(json);
            } else if (wa.getStatus() == WorkflowActionDb.STATE_IGNORED) {
                com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
                json.put("ID", wa.getInternalName());
                ignoreActions.add(json);
            } else if (wa.getStatus() == WorkflowActionDb.STATE_DISCARDED) {
                com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
                json.put("ID", wa.getInternalName());
                discardActions.add(json);
            } else if (wa.getStatus() == WorkflowActionDb.STATE_RETURN) {
                com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
                json.put("ID", wa.getInternalName());
                returnActions.add(json);
            }
        }

        jsonResult.put("activeActions", activeActions);
        jsonResult.put("finishActions", finishActions);
        jsonResult.put("ignoreActions", ignoreActions);
        jsonResult.put("discardActions", discardActions);
        jsonResult.put("returnActions", returnActions);

        jsonResult.put("STATE_FINISHED", WorkflowActionDb.STATE_FINISHED);
        jsonResult.put("STATE_DOING", WorkflowActionDb.STATE_DOING);
        jsonResult.put("STATE_IGNORED", WorkflowActionDb.STATE_IGNORED);
        jsonResult.put("STATE_RETURN", WorkflowActionDb.STATE_RETURN);
        jsonResult.put("STATE_DISCARDED", WorkflowActionDb.STATE_DISCARDED);

        com.alibaba.fastjson.JSONArray aryMyAction = new com.alibaba.fastjson.JSONArray();
        MyActionDb mad = new MyActionDb();
        WorkflowActionDb wa = new WorkflowActionDb();
        Vector<MyActionDb> v = mad.getMyActionDbOfFlow(flowId);
        for (MyActionDb myActionDb : v) {
            com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
            wa = wa.getWorkflowActionDb((int)myActionDb.getActionId());

            json.put("receiveTime", myActionDb.getReceiveDate().getTime());
            json.put("internalName", wa.getInternalName());
            json.put("actionStatus", myActionDb.getActionStatus());
            json.put("checkTime", myActionDb.getCheckDate()!=null?myActionDb.getCheckDate().getTime()+"":"999999999999999");
            json.put("checkStatus", myActionDb.isChecked()?WorkflowActionDb.STATE_FINISHED:myActionDb.getActionStatus());
            aryMyAction.add(json);
        }
        jsonResult.put("aryMyAction", aryMyAction);

        /*String cloudUrl = cfg.get("cloudUrl");
        ILicense iLicense = SpringUtil.getBean(ILicense.class);
        if (iLicense.getCategory().equals(ConstUtil.CATEGORY_LOCAL)) {
            cloudUrl = request.getContextPath();
        }
        jsonResult.put("cloudUrl", cloudUrl);*/

        return new Result<>(jsonResult);
    }

    @ApiOperation(value = "更新流程标题", notes = "更新流程标题", httpMethod = "POST")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/modifyTitle", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> modifyTitle() {
        boolean re;
        try {
            int flowId = ParamUtil.getInt(request, "flowId");
            String typeCode = ParamUtil.get(request, "typeCode");
            String title = ParamUtil.get(request, "title");
            int level = ParamUtil.getInt(request, "level", WorkflowDb.LEVEL_NORMAL);
            WorkflowMgr wm = new WorkflowMgr();
            WorkflowDb wf = wm.getWorkflowDb(flowId);
            wf.setTypeCode(typeCode);
            wf.setTitle(title);
            wf.setLevel(level);
            re = wf.save();
        }
        catch (ErrMsgException e) {
            return new Result<>(false, e.getMessage());
        }
        return new Result<>(re);
    }

    @ApiOperation(value = "取流程导出中已设置的字段", notes = "取流程导出中已设置的字段", httpMethod = "POST")
    @ApiImplicitParam(name = "typeCode", value = "流程类型", required = true, dataType = "String")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/getExportFields", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> getExportFields(@RequestParam(value = "typeCode") String typeCode) {
        Leaf lf = new Leaf();
        lf = lf.getLeaf(typeCode);
        if (lf == null) {
            return new Result<>(false, "流程类型:" + typeCode + "不存在");
        }
        FormDb fd = new FormDb();
        fd = fd.getFormDb(lf.getFormCode());
        if (!fd.isLoaded()) {
            return new Result<>(false, "表单不存在");
        }

        Vector<FormField> v = fd.getFields();
        List<Object> list = new ArrayList<>();

        String exportColProps = lf.getExportColProps();
        if ("".equals(exportColProps)) {
            List<String> listName = new ArrayList<>();
            for (FormField ff : v) {
                list.add(JSON.toJSON(ff));

                listName.add(ff.getName());
            }
            lf.setExportColProps(String.join(",", listName.stream().map(String::valueOf).collect(Collectors.toList())));
            lf.update();
        } else {
            String[] ary = StrUtil.split(exportColProps, ",");
            if (ary != null) {
                for (String fieldName : ary) {
                    if (fieldName.contains(":")) {
                        String[] aryNest = fieldName.split(":");
                        FormDb fdNest = fd.getFormDb(aryNest[0]);
                        FormField ffNest = fdNest.getFormField(aryNest[1]);
                        if (ffNest == null) {
                            LogUtil.getLog(getClass()).warn("嵌套表: " + fdNest.getName() + " 中字段: " + fieldName + " 不存在");
                        } else {
                            JSONObject jsonObject = (JSONObject) JSON.toJSON(ffNest);
                            jsonObject.put("name", fdNest.getCode() + ":" + ffNest.getName());
                            jsonObject.put("title", fdNest.getName() + ":" + ffNest.getTitle());
                            list.add(jsonObject);
                        }
                    } else {
                        FormField ff = fd.getFormField(fieldName);
                        if (ff == null) {
                            LogUtil.getLog(getClass()).warn("字段: " + fieldName + " 不存在");
                        } else {
                            list.add(JSON.toJSON(ff));
                        }
                    }
                }
            }
        }
        return new Result<>(list);
    }

    @ApiOperation(value = "导出Excel", notes = "导出Excel", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "displayMode", value = "模式（待办、我参与的、我的）", required = true, dataType = "String"),
            @ApiImplicitParam(name = "typeCode", value = "流程类型", required = true, dataType = "String"),
            @ApiImplicitParam(name = "fields", value = "需导出的字段，以逗号分隔", required = false, dataType = "String")
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/flow/exportExcel", method = RequestMethod.POST)
    public Callable<String> exportExcel(HttpServletResponse response, @RequestParam(value="displayMode", defaultValue = "2")int displayMode, @RequestParam(value="typeCode") String typeCode, @RequestParam(value = "fields", required = false) String fields) throws ErrMsgException {
        if (WorkflowMgr.DISPLAY_MODE_SEARCH == displayMode) {
            if (typeCode == null) {
                throw new ErrMsgException("流程类型不能为空");
            }
        }

        return () -> {
            String uid = RandomSecquenceCreator.getId(20);

            if (WorkflowMgr.DISPLAY_MODE_SEARCH == displayMode) {
                workflowService.exportExcelForSearch(request, response, typeCode, fields, uid);
            } else {
                workflowService.exportExcel(request, response, typeCode, displayMode, uid);
            }
            return "success";
        };
    }

    @ApiOperation(value = "异步导出Excel", notes = "异步导出Excel", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "displayMode", value = "模式（待办、我参与的、我的）", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "typeCode", value = "流程类型", required = true, dataType = "String"),
            @ApiImplicitParam(name = "fields", value = "需导出的字段，以逗号分隔", required = false, dataType = "String")
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @RequestMapping(value = "/flow/exportExcelAsync", method = RequestMethod.POST)
    @ResponseBody
    public Result<Object> exportExcelAsync(HttpServletResponse response, @RequestParam(value="displayMode", defaultValue = "2")Integer displayMode, @RequestParam(value="typeCode") String typeCode, @RequestParam(value = "fields", required = false) String fields) throws IOException, ErrMsgException {
        if (WorkflowMgr.DISPLAY_MODE_SEARCH == displayMode) {
            if (typeCode == null) {
                throw new ErrMsgException("流程类型不能为空");
            }
        }

        String uid = RandomSecquenceCreator.getId(20);

        ExportExcelItem exportExcelItem = new ExportExcelItem();
        exportExcelItem.setUid(uid);
        exportExcelItem.setRows(-1);
        exportExcelItem.setCurRow(0);
        exportExcelItem.setId(-1);
        exportExcelCache.put(uid, exportExcelItem);

        final SecurityContext securityContext = SecurityContextHolder.getContext();

        if (WorkflowMgr.DISPLAY_MODE_SEARCH == displayMode) {
            workflowService.exportExcelForSearchAsync(response, typeCode, fields, securityContext, uid);
        } else {
            workflowService.exportExcelAsync(response, typeCode, displayMode, securityContext, uid);
        }
        return new Result<>(uid);
    }

    @ApiOperation(value = "取得测试信息", notes = "取得测试信息", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "myActionId", value = "待办记录ID", required = true, dataType = "Long"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/getTestInfo", method={RequestMethod.POST}, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> getTestInfo(@RequestParam(value = "myActionId") Long myActionId) {
        com.alibaba.fastjson.JSONObject jsonResult = new com.alibaba.fastjson.JSONObject();

        MyActionDb mad = new MyActionDb();
        mad = mad.getMyActionDb(myActionId);
        long actionId = mad.getActionId();
        WorkflowActionDb wad = new WorkflowActionDb();
        wad = wad.getWorkflowActionDb((int) actionId);

        WorkflowDb wf = new WorkflowDb();
        wf = wf.getWorkflowDb((int) mad.getFlowId());

        com.redmoon.oa.Config cfg = new com.redmoon.oa.Config();
        String flowExpireUnit = cfg.get("flowExpireUnit");
        jsonResult.put("flowExpireUnit", flowExpireUnit);

        boolean canUserSeeFlowChart = cfg.getBooleanProperty("canUserSeeFlowChart");
        jsonResult.put("canUserSeeFlowChart", canUserSeeFlowChart);

        if (canUserSeeFlowChart) {
            jsonResult.put("flowJson", wf.getFlowJson());
        }

        com.alibaba.fastjson.JSONArray aryNextActions = new com.alibaba.fastjson.JSONArray();
        jsonResult.put("nextActions", aryNextActions);

        UserDb user = new UserDb();
        for (WorkflowActionDb nextwad : wad.getLinkToActions()) {
            com.alibaba.fastjson.JSONObject jsonAction = new com.alibaba.fastjson.JSONObject();
            jsonAction.put("actionTitle", nextwad.getTitle());
            com.alibaba.fastjson.JSONArray ary = new com.alibaba.fastjson.JSONArray();
            jsonAction.put("checkers", ary);
            Iterator<MyActionDb> irmad = mad.getActionDoing(nextwad.getId()).iterator();
            while (irmad.hasNext()) {
                MyActionDb nextmad = irmad.next();
                com.alibaba.fastjson.JSONObject checker = new com.alibaba.fastjson.JSONObject();
                checker.put("nextMyActionId", nextmad.getId());
                checker.put("userName", nextmad.getUserName());
                checker.put("realName", user.getUserDb(nextmad.getUserName()).getRealName());
                ary.add(checker);
            }
            aryNextActions.add(jsonAction);
        }

        com.alibaba.fastjson.JSONArray allActions = new com.alibaba.fastjson.JSONArray();

        for (MyActionDb nextmad : mad.getFlowDoingWithoutAction(mad.getFlowId())) {
            WorkflowActionDb nextwad = wad.getWorkflowActionDb((int) nextmad.getActionId());
            com.alibaba.fastjson.JSONObject jsonAction = new com.alibaba.fastjson.JSONObject();
            jsonAction.put("actionTitle", nextwad.getTitle());
            jsonAction.put("nextMyActionId", nextmad.getId());
            jsonAction.put("userName", nextmad.getUserName());
            jsonAction.put("realName", user.getUserDb(nextmad.getUserName()).getRealName());
            allActions.add(jsonAction);
        }
        jsonResult.put("allActions", allActions);
        jsonResult.put("statusDesc", wf.getStatusDesc());
        jsonResult.put("flowId", wf.getId());
        jsonResult.put("title", wf.getTitle());
        return new Result<>(jsonResult);
    }

    @ApiOperation(value = "为用户生成token", notes = "为用户生成token，用于流程调试模式", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "newUserName", value = "用户名", required = true, dataType = "String"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @RequestMapping(value = "/flow/getTokenByUser", method= RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    @ResponseBody
    public Result<Object> getTokenByUser(@RequestParam(value = "newUserName") String newUserName) {
        Result<Object> result = new Result<>(true);
        result.setData(jwtUtil.generate(newUserName));
        return result;
    }

    @ApiOperation(value = "催办", notes = "催办", httpMethod = "POST")
    @RequestMapping(value = "/flow/remind", method= RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    @ResponseBody
    public Result<Object> remind(@RequestParam(value = "myActionId") long myActionId) throws ErrMsgException {
        MyActionDb myActionDb = new MyActionDb();
        myActionDb = myActionDb.getMyActionDb(myActionId);
        String userName = myActionDb.getUserName();
        WorkflowDb wf = new WorkflowDb();
        wf = wf.getWorkflowDb((int)myActionDb.getFlowId());
        String myUserName = authUtil.getUserName();
        User user = userCache.getUser(myUserName);
        String title = user.getRealName() + " 催办：" + wf.getTitle();
        WorkflowActionDb wa = new WorkflowActionDb();
        wa = wa.getWorkflowActionDb((int)myActionDb.getActionId());
        String content = WorkflowMgr.getFormAbstractTable(wf, wa);

        boolean isToMobile = SMSFactory.isUseSMS();
        if (isToMobile) {
            ProxyFactory proxyFactory = new ProxyFactory("com.redmoon.oa.message.MessageDb");
            IMessage imsg = (IMessage) proxyFactory.getProxy();
            imsg.sendSysMsg(userName, title, content, MessageDb.ACTION_FLOW_DISPOSE, "", String.valueOf(myActionDb.getId()));
        }
        else {
            // 发送信息
            MessageDb md = new MessageDb();
            md.sendSysMsg(userName, title, content, MessageDb.ACTION_FLOW_DISPOSE, "", String.valueOf(myActionDb.getId()));
        }
        return new Result<>(true);
    }

    @ApiOperation(value = "抄送给我的列表", notes = "抄送给我的列表", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "op", value = "操作类型", required = true, dataType = "String"),
            @ApiImplicitParam(name = "title", value = "标题", required = true, dataType = "String"),
            @ApiImplicitParam(name = "page", value = "页数", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "pageSize", value = "每页条数", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "fromDate", value = "开始日期", required = true, dataType = "Date"),
            @ApiImplicitParam(name = "toDate", value = "结束日期", required = true, dataType = "Date"),
            @ApiImplicitParam(name = "field", value = "排序字段", required = true, dataType = "String"),
            @ApiImplicitParam(name = "order", value = "排序顺序", required = true, dataType = "String"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/listDistributeToMe", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> listDistributeToMe(@RequestParam(value = "op", required = false) String op, @RequestParam(value = "title", required = false) String title, @RequestParam(value="page", required = false, defaultValue = "1") Integer page, @RequestParam(value="pageSize", required = false, defaultValue = "20") Integer pageSize,
                                             @RequestParam(value = "fromDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date fromDate,
                                             @RequestParam(value = "toDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date toDate,
                                             @RequestParam(value = "opType", required = false, defaultValue = "0") Integer opType,
                                             @RequestParam(value = "field", required = false) String field, @RequestParam(value = "order", required = false) String order) {
        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        com.alibaba.fastjson.JSONArray list = new com.alibaba.fastjson.JSONArray();

        String userName = authUtil.getUserName();
        WorkflowDb wf = new WorkflowDb();
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        com.redmoon.oa.sso.Config ssoconfig = new com.redmoon.oa.sso.Config();
        String desKey = ssoconfig.get("key");
        ListResult lr = workflowService.listDistributeToMe(op, title, fromDate, toDate, page, pageSize, field, order, opType);
        for (Object o : lr.getResult()) {
            PaperDistributeDb pdd = (PaperDistributeDb)o;
            com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject();
            jsonObject.put("title", pdd.getString("title"));
            String unit = pdd.getString("from_unit");
            String unitName = "";
            if (!StrUtil.isEmpty(unit)) {
                unitName = departmentCache.getDepartment(pdd.getString("from_unit")).getName();
            }
            String realName = userCache.getUser(pdd.getString("user_name")).getRealName();
            jsonObject.put("unitName", unitName);
            jsonObject.put("realName", realName);
            jsonObject.put("flowId", pdd.getInt("flow"));

            boolean canDel = false;
            if (pdd.getString("user_name").equals(authUtil.getUserName())) {
                canDel = true;
            } else {
                if (!privilege.isUserPrivValid(request, ConstUtil.PRIV_ADMIN)) {
                    wf = wf.getWorkflowDb(pdd.getInt("flow"));
                    LeafPriv lp = new LeafPriv(wf.getTypeCode());
                    if (privilege.isUserPrivValid(request, "admin.flow")) {
                        if (lp.canUserExamine(userName)) {
                            canDel = true;
                        }
                    }
                } else {
                    canDel = true;
                }
            }
            jsonObject.put("canDel", canDel);

            jsonObject.put("disDate", DateUtil.format(pdd.getDate("dis_date"), "yyyy-MM-dd HH:mm:ss"));

            int actionId = pdd.getInt("action_id");
            if (actionId != -1) {
                WorkflowActionDb wad = new WorkflowActionDb();
                wad = wad.getWorkflowActionDb(actionId);
                jsonObject.put("actionTitle", wad.getTitle());
                jsonObject.put("actionColorIndex", wad.getOfficeColorIndex());
            } else {
                jsonObject.put("actionTitle", "");
                jsonObject.put("actionColorIndex", 1);
            }

            String visitKey = ThreeDesUtil.encrypt2hex(desKey, String.valueOf(pdd.getInt("flow")));
            jsonObject.put("visitKey", visitKey);

            String toUnit = pdd.getString("to_unit");
            User user = userCache.getUser(toUnit);
            if (user != null) {
                jsonObject.put("toRealName", user.getRealName());
            } else {
                jsonObject.put("toRealName", "不存在");
            }

            list.add(jsonObject);
        }
        json.put("list", list);
        json.put("total", lr.getTotal());
        return new Result<>(json);
    }

    @ApiOperation(value = "流程抄送列表", notes = "流程抄送列表", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "op", value = "操作类型", required = true, dataType = "String"),
            @ApiImplicitParam(name = "title", value = "标题", required = true, dataType = "String"),
            @ApiImplicitParam(name = "page", value = "页数", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "pageSize", value = "每页条数", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "fromDate", value = "开始日期", required = true, dataType = "Date"),
            @ApiImplicitParam(name = "toDate", value = "结束日期", required = true, dataType = "Date"),
            @ApiImplicitParam(name = "field", value = "排序字段", required = true, dataType = "String"),
            @ApiImplicitParam(name = "order", value = "排序顺序", required = true, dataType = "String"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/listDistributeByFlow", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
    public Result<Object> listDistributeByFlow(@RequestParam(value = "flowId") Integer flowId, @RequestParam(value = "op", required = false) String op, @RequestParam(value = "title", required = false) String title, @RequestParam(value="page", required = false, defaultValue = "1") Integer page, @RequestParam(value="pageSize", required = false, defaultValue = "20") Integer pageSize, @RequestParam(value = "fromDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date fromDate, @RequestParam(value = "toDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date toDate, @RequestParam(value = "field", required = false) String field, @RequestParam(value = "order", required = false) String order) {
        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        com.alibaba.fastjson.JSONArray list = new com.alibaba.fastjson.JSONArray();

        com.redmoon.oa.sso.Config ssoconfig = new com.redmoon.oa.sso.Config();
        String desKey = ssoconfig.get("key");
        ListResult lr = workflowService.listDistributeByFlow(flowId, op, title, fromDate, toDate, page, pageSize, field, order);
        for (Object o : lr.getResult()) {
            PaperDistributeDb pdd = (PaperDistributeDb)o;
            com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject();
            jsonObject.put("title", pdd.getString("title"));
            String unit = pdd.getString("from_unit");
            String unitName = "";
            if (!StrUtil.isEmpty(unit)) {
                unitName = departmentCache.getDepartment(pdd.getString("from_unit")).getName();
            }
            String realName = userCache.getUser(pdd.getString("user_name")).getRealName();
            jsonObject.put("unitName", unitName);
            jsonObject.put("realName", realName);
            jsonObject.put("flowId", pdd.getInt("flow"));
            jsonObject.put("disDate", DateUtil.format(pdd.getDate("dis_date"), "yyyy-MM-dd HH:mm:ss"));

            String visitKey = ThreeDesUtil.encrypt2hex(desKey, String.valueOf(pdd.getInt("flow")));
            jsonObject.put("visitKey", visitKey);

            String toUnit = pdd.getString("to_unit");
            User user = userCache.getUser(toUnit);
            if (user != null) {
                jsonObject.put("toRealName", user.getRealName());
                jsonObject.put("photo", user.getPhoto());
                jsonObject.put("gender", user.getGender());
            } else {
                jsonObject.put("toRealName", "不存在");
                jsonObject.put("gender", false);
            }

            int actionId = pdd.getInt("action_id");
            if (actionId != -1) {
                WorkflowActionDb wad = new WorkflowActionDb();
                wad = wad.getWorkflowActionDb(actionId);
                jsonObject.put("actionTitle", wad.getTitle());
                jsonObject.put("actionColorIndex", wad.getOfficeColorIndex());
            } else {
                jsonObject.put("actionTitle", "");
                jsonObject.put("actionColorIndex", 1);
            }

            jsonObject.put("id", pdd.getLong("id"));

            list.add(jsonObject);
        }
        json.put("list", list);
        json.put("total", lr.getTotal());
        return new Result<>(json);
    }

    @ApiOperation(value = "我抄送的列表", notes = "我抄送的列表", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "op", value = "操作类型", required = true, dataType = "String"),
            @ApiImplicitParam(name = "title", value = "标题", required = true, dataType = "String"),
            @ApiImplicitParam(name = "page", value = "页数", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "pageSize", value = "每页条数", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "fromDate", value = "开始日期", required = true, dataType = "Date"),
            @ApiImplicitParam(name = "toDate", value = "结束日期", required = true, dataType = "Date"),
            @ApiImplicitParam(name = "field", value = "排序字段", required = true, dataType = "String"),
            @ApiImplicitParam(name = "order", value = "排序顺序", required = true, dataType = "String"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/listMyDistribute", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public Result<Object> listMyDistribute(@RequestParam(value = "op", required = false) String op, @RequestParam(value = "title", required = false) String title,
                                           @RequestParam(value = "realName", required = false) String realName,
                                           @RequestParam(value="page", required = false, defaultValue = "1") Integer page,
                                           @RequestParam(value="pageSize", required = false, defaultValue = "20") Integer pageSize,
                                           @RequestParam(value = "fromDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date fromDate,
                                           @RequestParam(value = "toDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date toDate,
                                           @RequestParam(value = "opType", required = false, defaultValue = "0") Integer opType,
                                           @RequestParam(value = "field", required = false) String field, @RequestParam(value = "order", required = false) String order) {
        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        com.alibaba.fastjson.JSONArray list = new com.alibaba.fastjson.JSONArray();

        com.redmoon.oa.sso.Config ssoconfig = new com.redmoon.oa.sso.Config();
        String desKey = ssoconfig.get("key");
        ListResult lr = workflowService.listMyDistribute(op, title, realName, fromDate, toDate, page, pageSize, field, order, opType);
        for (Object o : lr.getResult()) {
            PaperDistributeDb pdd = (PaperDistributeDb)o;
            com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject();
            jsonObject.put("title", pdd.getString("title"));
            String unit = pdd.getString("from_unit");
            String unitName = "";
            if (!StrUtil.isEmpty(unit)) {
                unitName = departmentCache.getDepartment(pdd.getString("from_unit")).getName();
            }
            String realNamePage = userCache.getUser(pdd.getString("user_name")).getRealName();
            jsonObject.put("id", pdd.getLong("id"));
            jsonObject.put("unitName", unitName);
            jsonObject.put("realName", realNamePage);
            jsonObject.put("flowId", pdd.getInt("flow"));
            jsonObject.put("disDate", DateUtil.format(pdd.getDate("dis_date"), "yyyy-MM-dd HH:mm:ss"));
            String visitKey = ThreeDesUtil.encrypt2hex(desKey, String.valueOf(pdd.getInt("flow")));
            jsonObject.put("visitKey", visitKey);

            String toUnit = pdd.getString("to_unit");
            User user = userCache.getUser(toUnit);
            if (user != null) {
                jsonObject.put("toRealName", user.getRealName());
            } else {
                jsonObject.put("toRealName", "不存在");
            }

            int actionId = pdd.getInt("action_id");
            if (actionId != -1) {
                WorkflowActionDb wad = new WorkflowActionDb();
                wad = wad.getWorkflowActionDb(actionId);
                jsonObject.put("actionTitle", wad.getTitle());
                jsonObject.put("actionColorIndex", wad.getOfficeColorIndex());
            } else {
                jsonObject.put("actionTitle", "");
                jsonObject.put("actionColorIndex", 1);
            }

            list.add(jsonObject);
        }
        json.put("list", list);
        json.put("total", lr.getTotal());
        return new Result<>(json);
    }

    @ApiOperation(value = "删除抄送", notes = "删除抄送", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "ID", required = true, dataType = "Long"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/delDistribute", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> delDistribute(@RequestParam(value = "id") Long id) throws ValidateException {
        PaperDistributeDb pdd = new PaperDistributeDb();
        pdd = pdd.getPaperDistributeDb(id);

        boolean canDel = false;
        if (pdd.getString("user_name").equals(authUtil.getUserName())) {
            canDel = true;
        } else {
            com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
            if (!privilege.isUserPrivValid(request, ConstUtil.PRIV_ADMIN)) {
                WorkflowDb wf = new WorkflowDb();
                wf = wf.getWorkflowDb(pdd.getInt("flow"));
                LeafPriv lp = new LeafPriv(wf.getTypeCode());
                if (privilege.isUserPrivValid(request, "admin.flow")) {
                    if (lp.canUserExamine(authUtil.getUserName())) {
                        canDel = true;
                    }
                }
            } else {
                canDel = true;
            }
        }
        if (!canDel) {
            throw new ValidateException("非法操作");
        }
        try {
            return new Result<>(pdd.del());
        } catch (ResKeyException e) {
            LogUtil.getLog(getClass()).error(e);
            throw new ErrMsgException(e.getMessage(request));
        }
    }

    @ApiOperation(value = "获得用户有权限的公文模板", notes = "获得用户有权限的公文模板", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/getTemplates", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> getTemplates(@RequestParam Integer flowId) {
        WorkflowDb wf = new WorkflowDb();
        wf = wf.getWorkflowDb(flowId);
        String typeCode = wf.getTypeCode();
        Leaf lf = new Leaf();
        lf = lf.getLeaf(typeCode);
        DocTemplateDb ld = new DocTemplateDb();
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        String sql = "select id from " + ld.getTableName() + " where unit_code=" + StrUtil.sqlstr(com.redmoon.oa.flow.Leaf.UNIT_CODE_PUBLIC) + " or unit_code=" + StrUtil.sqlstr(privilege.getUserUnitCode(request)) + " order by sort";
        com.alibaba.fastjson.JSONArray ary = new com.alibaba.fastjson.JSONArray();
        DocTemplateMgr dtm = new DocTemplateMgr();
        for (Object o : ld.list(sql)) {
            ld = (DocTemplateDb) o;
            String[] arr = StrUtil.split(ld.getTypeCodes(), ",");
            boolean isValid = false;
            boolean isFlowValid = false;
            if (arr != null) {
                List<String> list = Arrays.asList(arr);
                if (list.contains(typeCode) || list.contains(lf.getParentCode())) {
                    isFlowValid = true;
                }

                if (isFlowValid) {
                    if (!StrUtil.isEmpty(ld.getDepts())) {
                        // 如果流程匹配，且部门不为空也匹配
                        if (dtm.canUserSee(request, ld)) {
                            isValid = true;
                        }
                    } else {
                        isValid = true;
                    }
                }
            } else {
                // 如果未设流程，则以部门为准
                if (dtm.canUserSee(request, ld)) {
                    isValid = true;
                }
            }

            if (isValid) {
                com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
                json.put("id", ld.getId());
                json.put("name", ld.getTitle());
                ary.add(json);
            }
        }
        return new Result<>(ary);
    }

    @ApiOperation(value = "获得所有公文模板", notes = "获得所有公文模板", httpMethod = "POST")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/listTemplate", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> listTemplate() {
        DocTemplateDb ld = new DocTemplateDb();
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        String sql = "select id from " + ld.getTableName() + " where unit_code=" + StrUtil.sqlstr(com.redmoon.oa.flow.Leaf.UNIT_CODE_PUBLIC) + " or unit_code=" + StrUtil.sqlstr(privilege.getUserUnitCode(request)) + " order by sort";
        com.alibaba.fastjson.JSONArray ary = new com.alibaba.fastjson.JSONArray();
        DocTemplateMgr dtm = new DocTemplateMgr();
        for (Object o : ld.list(sql)) {
            ld = (DocTemplateDb) o;
            com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
            json.put("id", ld.getId());
            json.put("title", ld.getTitle());
            ary.add(json);
        }
        return new Result<>(ary);
    }

    @ApiOperation(value = "模板套红", notes = "模板套红", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "attachId", value = "附件ID", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "templateId", value = "模板ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/convertToRedDocument", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> convertToRedDocument(@RequestParam(value = "flowId") Integer flowId, @RequestParam(value = "attachId") Integer attachId, @RequestParam(value = "templateId") Integer templateId) {
        if (!licenseUtil.isEnabled(LicenseConstUtil.INRED)) {
            return new Result<>(false, licenseUtil.getCompErrMsg(LicenseConstUtil.INRED));
        }
        Config cfg = Config.getInstance();
        int inRedMode = cfg.getInt("inRedMode");
        return new Result<>(workflowPlusService.convertToRedDocument(flowId, attachId, templateId, inRedMode));
    }

    @ApiOperation(value = "获得用户有权限的印章", notes = "获得用户有权限的印章", httpMethod = "POST")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/getStamps", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> getStamps() {
        com.alibaba.fastjson.JSONArray ary = new com.alibaba.fastjson.JSONArray();
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        StampPriv sp = new StampPriv();
        Map<String, String> map = new HashMap<>();
        Vector<StampDb> v = sp.getStampsOfUser(privilege.getUser(request));
        for (StampDb sd : v) {
            if (sd != null && sd.isLoaded()) {
                if (map.containsKey(String.valueOf(sd.getId()))) {
                    continue;
                }
                com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
                map.put(String.valueOf(sd.getId()), "");
                json.put("id", sd.getId());
                json.put("name", sd.getTitle());
                json.put("imageUrl", sd.getImageUrl());
                ary.add(json);
            }
        }

        return new Result<>(ary);
    }

    @ApiOperation(value = "文件盖章", notes = "文件盖章", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "attachId", value = "附件ID", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "stampId", value = "印章ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/sealDocument", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> sealDocument(@RequestParam(value = "flowId") Integer flowId, @RequestParam(value = "attachId") Integer attachId, @RequestParam(value = "stampId") Integer stampId) {
        WorkflowDb wf = new WorkflowDb();
        wf = wf.getWorkflowDb(flowId);
        Attachment att = wf.getDocument().getAttachment(1, attachId);
        String attchFilePath = sysUtil.getUploadPath() + att.getVisualPath() + "/" + att.getDiskName();

        try {
            poiTlUtil.sealDocumentByBookmark(attchFilePath, stampId);

            // 跟套红不一样，一个文件可能会被加盖多个印章，故此处的处理意义不大
            att.setSealed(true);
            att.save();

            // 更新缓存
            DocContentCacheMgr dcm = new DocContentCacheMgr();
            dcm.refreshUpdate(wf.getDocId(), 1);

            String ext = StrUtil.getFileExt(att.getDiskName());
            com.redmoon.oa.Config cfg = Config.getInstance();
            boolean canOfficeFilePreview = cfg.getBooleanProperty("canOfficeFilePreview");
            if (canOfficeFilePreview) {
                if ("doc".equals(ext) || "docx".equals(ext) || "xls".equals(ext) || "xlsx".equals(ext)) {
                    PreviewUtil.createOfficeFilePreviewHTML(attchFilePath);

                    if (sysProperties.isObjStoreEnabled()) {
                        // 删除本地文件
                        if (!sysProperties.isObjStoreReserveLocalFile()) {
                            File f = new File(attchFilePath);
                            f.delete();
                        }
                    }
                }
            }

            return new Result<>(true);
        } catch (IOException | ErrMsgException | InvalidFormatException e) {
            LogUtil.getLog(getClass()).error(e);
            return new Result<>(false, e.getMessage());
        }
    }

    @ApiOperation(value = "取得流程中已处理的节点", notes = "取得流程中已处理的节点，按时间降序排序", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/getActionsFinished", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> getActionsFinished(@RequestParam(value = "flowId") Integer flowId) {
        WorkflowActionDb wad = new WorkflowActionDb();
        List<WorkflowActionDb> list = wad.getActionsFinishedOfFlow(flowId);
        JSONArray arr = new JSONArray();
        for (WorkflowActionDb wa : list) {
            JSONObject json = new JSONObject();
            json.put("id", wa.getId());
            json.put("name", wa.getTitle());
            arr.add(json);
        }
        return new Result<>(arr);
    }

    @ApiOperation(value = "取得流程中已处理的办理记录", notes = "取得流程中已处理的办理记录，按时间降序排序", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/getMyActionsFinished", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> getMyActionsFinished(@RequestParam(value = "flowId") Integer flowId) {
        MyActionDb myActionDb = new MyActionDb();
        WorkflowActionDb wad = new WorkflowActionDb();
        List<MyActionDb> list = myActionDb.getMyActionDbChecked(flowId);
        JSONArray arr = new JSONArray();
        for (MyActionDb mad : list) {
            JSONObject json = new JSONObject();
            json.put("id", mad.getId());
            json.put("userName", mad.getUserName());
            json.put("realName", userCache.getUser(mad.getUserName()).getRealName());
            wad = wad.getWorkflowActionDb((int)mad.getActionId());
            json.put("actionTitle", wad.getTitle());
            json.put("actionJobName", wad.getJobName());
            arr.add(json);
        }
        return new Result<>(arr);
    }

    @ApiOperation(value = "取得全部流程目录树", notes = "取得全部流程目录树", httpMethod = "POST")
    @ApiImplicitParam(name = "parentCode", value = "父节点编码", required = false, dataType = "String")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/getDirTreeAll", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<com.alibaba.fastjson.JSONArray> getDirTreeAll(@RequestParam(value="parentCode", required = false) String parentCode) {
        if (parentCode == null) {
            parentCode = Leaf.CODE_ROOT;
        }
        Leaf lf = new Leaf();
        lf = lf.getLeaf(parentCode);
        DirectoryView dv = new DirectoryView(lf);
        com.alibaba.fastjson.JSONObject jsonRoot = new com.alibaba.fastjson.JSONObject();
        // 如果是根节点，则将code置为空，以免在前台看到是“全部类别”却查不出来引起误解
        jsonRoot.put("code", lf.getCode());
        jsonRoot.put("parentCode", lf.getParentCode());
        jsonRoot.put("name", lf.getName());
        jsonRoot.put("layer", lf.getLayer());
        jsonRoot.put("isOpen", lf.isOpen());
        jsonRoot.put("type", lf.getType());
        jsonRoot.put("formCode", lf.getFormCode());
        dv.getTreeAll(lf, jsonRoot);

        com.alibaba.fastjson.JSONArray ary = new com.alibaba.fastjson.JSONArray();
        ary.add(jsonRoot);
        return new Result<>(ary);
    }

    @ApiOperation(value = "取得OBS下载url", notes = "取得OBS下载url", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "key", value = "Key", required = true, dataType = "String"),
            @ApiImplicitParam(name = "fileName", value = "文件名", required = true, dataType = "String"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/getObsDownloadUrl", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<com.alibaba.fastjson.JSONObject> getObsDownloadUrl(@RequestParam(value="key") String key, @RequestParam String fileName) {
        IObsService obsService = obsServiceFactory.getInstance();
        JSONObject json = new JSONObject();
        json.put("url", obsService.getDownloadUrl(key, fileName));
        return new Result<>(json);
    }

    @ApiOperation(value = "后台获取流程记录", notes = "后台获取流程记录", httpMethod = "POST")
    @ApiImplicitParam(name = "typeCode", value = "流程类型节点编码", required = true, dataType = "String")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/listRecord", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<com.alibaba.fastjson.JSONObject> listRecord(@RequestParam(value="typeCode") String typeCode) {
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        LeafPriv lp = new LeafPriv(typeCode);
        if (lp.canUserQuery(privilege.getUser(request)) || lp.canUserExamine(privilege.getUser(request))) {
            ;
        }
        else {
            throw new ErrMsgException(i18nUtil.get("pvg_invalid"));
        }

        int pageSize = ParamUtil.getInt(request, "pageSize", 20);
        int curPage = ParamUtil.getInt(request, "page", 1);

        String op = ParamUtil.get(request, "op");
        String by = ParamUtil.get(request, "by");
        String what = ParamUtil.get(request, "what");
        String status = ParamUtil.get(request, "status");

        String fromDate = ParamUtil.get(request, "fromDate");
        String toDate = ParamUtil.get(request, "toDate");

        WorkflowDb wf = new WorkflowDb();

        // String sql = "select id from flow where status<>" + WorkflowDb.STATUS_NONE + " and status<>" + WorkflowDb.STATUS_DELETED;
        String sql = "select id from flow where status>" + WorkflowDb.STATUS_NONE;
        if ("search".equals(op)) {
            if ("user".equals(by)) {
                // sql = "select id from flow where username like " + StrUtil.sqlstr("%" + what + "%") +  " order by begin_date desc";
                if (!"".equals(what)) {
                    // 有管理权，则可在全部流程中搜索
                    if (privilege.isUserPrivValid(request, "admin") && "".equals(typeCode)) {
                        sql = "select f.id from flow f, users u where f.username=u.name and u.realName like " + StrUtil.sqlstr("%" + what + "%");
                    }
                    else {
                        if (!Leaf.CODE_ROOT.equals(typeCode)) {
                            sql = "select f.id from flow f, users u where f.username=u.name and f.type_code=" + StrUtil.sqlstr(typeCode) + " and u.realName like " + StrUtil.sqlstr("%" + what + "%");
                        }
                        else {
                            sql = "select f.id from flow f, users u where f.username=u.name and u.realName like " + StrUtil.sqlstr("%" + what + "%");
                        }
                    }
                }
                else {
                    // 有管理权，则可在全部流程中搜索
                    if (privilege.isUserPrivValid(request, "admin") && "".equals(typeCode)) {
                        sql = "select f.id from flow f where 1=1";
                    }
                    else {
                        if (!Leaf.CODE_ROOT.equals(typeCode)) {
                            sql = "select f.id from flow f where f.type_code=" + StrUtil.sqlstr(typeCode);
                        }
                        else {
                            sql = "select f.id from flow f where 1=1";
                        }
                    }
                }
            }
            else if ("title".equals(by)) {
                // 有管理权，则可在全部流程中搜索
                if (privilege.isUserPrivValid(request, "admin") && "".equals(typeCode)) {
                    sql = "select id from flow f where title like " + StrUtil.sqlstr("%" + what + "%");
                }
                else {
                    if (!Leaf.CODE_ROOT.equals(typeCode)) {
                        sql = "select id from flow f where title like " + StrUtil.sqlstr("%" + what + "%") + " and type_code=" + StrUtil.sqlstr(typeCode);
                    }
                    else {
                        sql = "select id from flow f where title like " + StrUtil.sqlstr("%" + what + "%") ;
                    }
                }
            }
            else if ("ID".equals(by)) {
                if (privilege.isUserPrivValid(request, "admin")) {
                    if ("".equals(what)) {
                        sql = "select id from flow f where 1=1";
                    }
                    else {
                        sql = "select id from flow f where id=" + StrUtil.toInt(what, -1);
                    }
                }
                else {
                    if ("".equals(what)) {
                        if (!Leaf.CODE_ROOT.equals(typeCode)) {
                            sql = "select id from flow f where 1=1 and type_code=" + StrUtil.sqlstr(typeCode);
                        }
                        else {
                            sql = "select id from flow f where 1=1";
                        }
                    }
                    else {
                        if (!Leaf.CODE_ROOT.equals(typeCode)) {
                            sql = "select id from flow f where id=" + what + " and type_code=" + StrUtil.sqlstr(typeCode);
                        }
                        else {
                            sql = "select id from flow f where id=" + what;
                        }
                    }
                }
            }

            if (!"".equals(fromDate) && !"".equals(toDate)) {
                java.util.Date d = DateUtil.parse(toDate, "yyyy-MM-dd");
                d = DateUtil.addDate(d, 1);
                String toDate2 = DateUtil.format(d, "yyyy-MM-dd");
                sql += " and (BEGIN_DATE>=" + SQLFilter.getDateStr(fromDate, "yyyy-MM-dd") + " and BEGIN_DATE<=" + SQLFilter.getDateStr(toDate2, "yyyy-MM-dd") + ")";
            }
            else if (!"".equals(fromDate)) {
                sql += " and BEGIN_DATE>=" + SQLFilter.getDateStr(fromDate, "yyyy-MM-dd");
            }
            else if (fromDate.equals("") && !"".equals(toDate)) {
                sql += " and BEGIN_DATE<=" + SQLFilter.getDateStr(toDate, "yyyy-MM-dd");
            }

            if (!"".equals(status)) {
                sql += " and status=" + status;
            } else {
                sql += " and status<>" + WorkflowDb.STATUS_NONE + " and status<>" + WorkflowDb.STATUS_DELETED;
            }
        }
        else {
            if (!"".equals(typeCode)) {
                /*
                LeafPriv lp = new LeafPriv(typeCode);
                if (!lp.canUserSee(privilege.getUser(request))) {
                    out.println(cn.js.fan.web.SkinUtil.makeErrMsg(request, cn.js.fan.web.SkinUtil.LoadString(request, "pvg_invalid")));
                    return;
                }
                */
                if (!Leaf.CODE_ROOT.equals(typeCode)) {
                    sql = "select id from flow f where type_code=" + StrUtil.sqlstr(typeCode);
                    sql += " and f.status<>" + WorkflowDb.STATUS_NONE + " and status<>" + WorkflowDb.STATUS_DELETED;
                }
                else {
                    sql = "select id from flow f where f.status<>" + WorkflowDb.STATUS_NONE + " and status<>" + WorkflowDb.STATUS_DELETED;
                }
            }
        }

        sql += " order by id desc";

        ListResult lr = wf.listResult(sql, curPage, pageSize);

        JSONObject jobject = new JSONObject();
        com.alibaba.fastjson.JSONArray rows = new com.alibaba.fastjson.JSONArray();
        jobject.put("list", rows);
        jobject.put("page", curPage);
        jobject.put("total", lr.getTotal());

        Leaf lf = new Leaf();
        FormDb fd = new FormDb();
        FormDAO dao = new FormDAO();
        boolean isAdmin = privilege.isUserPrivValid(request, "admin");

        for (Object o : lr.getResult()) {
            wf = (WorkflowDb) o;

            JSONObject json = new JSONObject();
            Leaf lf2 = lf.getLeaf(wf.getTypeCode());
            String typeName = lf2 != null ? lf2.getName() : "不存在";
            if (lf2 != null) {
                fd = fd.getFormDb(lf2.getFormCode());
                dao = dao.getFormDAOByCache(wf.getId(), fd);
                if (dao != null) {
                    json.put("fdaoId", dao.getId());
                } else {
                    dao = new FormDAO();
                    json.put("fdaoId", -1);
                }
            } else {
                json.put("fdaoId", -1);
            }

            json.put("id", wf.getId());
            json.put("title", wf.getTitle());
            json.put("typeName", typeName);
            json.put("beginDate", DateUtil.format(wf.getBeginDate(), "yyyy-MM-dd HH:mm:ss"));
            User user = userCache.getUser(wf.getUserName());
            if (user != null) {
                json.put("starter", user.getRealName());
            } else {
                json.put("starter", "不存在");
            }
            json.put("status", wf.getStatusDesc());
            json.put("isAdmin", isAdmin);
            if (dao.isLoaded()) {
                json.put("moduleCode", dao.getFormCode());
            }
            rows.add(json);
        }
        return new Result<>(jobject);
    }

    @ApiOperation(value = "流程月统计", notes = "流程月统计", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "typeCode", value = "流程类型节点编码", required = true, dataType = "String"),
            @ApiImplicitParam(name = "year", value = "年份", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "month", value = "月份", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @PreAuthorize("hasAnyAuthority('admin', 'admin.flow', 'admin.unit')")
    @ResponseBody
    @RequestMapping(value = "/flow/getStatMonth", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<com.alibaba.fastjson.JSONObject> getStatMonth(@RequestParam(value="typeCode") String typeCode, @RequestParam Integer year, @RequestParam Integer month) {
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        if (StrUtil.isEmpty(typeCode)) {
            if (!privilege.isUserPrivValid(request, "admin")) {
                throw new ErrMsgException(i18nUtil.get("pvg_invalid"));
            }
        }

        JSONObject json = new JSONObject();
        String leafName = "全部";
        if (!"".equals(typeCode)) {
            Leaf lf = new Leaf();
            lf = lf.getLeaf(typeCode);
            leafName = lf.getName();
        }

        Calendar cal = Calendar.getInstance();
        if (year == null) {
            year = cal.get(Calendar.YEAR);
        }
        if (month == null) {
            month = cal.get(Calendar.MONTH) + 1;
        }

        int totalWorkflow = 0;
        int days = DateUtil.getDayCount(year, month - 1);

        Calendar showCal = Calendar.getInstance();
        MyActionDb mad = new MyActionDb();
        Vector actionV;
        String sql;
        ArrayList al = new ArrayList();
        for (int i = 1; i <= days; i++) {
            showCal.set(year, month - 1, i);

            String bdate = SQLFilter.getDateStr(DateUtil.format(showCal, "yyyy-MM-dd 00:00:00"), "yyyy-MM-dd HH:mm:ss");
            Calendar nextCal = Calendar.getInstance();
            nextCal.set(year, month - 1, i + 1);
            String edate = SQLFilter.getDateStr(DateUtil.format(nextCal, "yyyy-MM-dd 00:00:00"), "yyyy-MM-dd HH:mm:ss");

            if ("".equals(typeCode)) {
                sql = "select id from flow where begin_date>=" + bdate + " and begin_date<" + edate + " order by begin_date desc";
            } else {
                sql = "select id from flow where type_code=" + StrUtil.sqlstr(typeCode) + " and begin_date>=" + bdate + " and begin_date<" + edate + " order by begin_date desc";
            }

            actionV = mad.list(sql);
            totalWorkflow += actionV.size();
            al.add(actionV.size());
        }

        json.put("list", al);
        json.put("title", leafName);
        json.put("total", totalWorkflow);
        return new Result<>(json);
    }

    @ApiOperation(value = "流程年统计", notes = "流程年统计", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "typeCode", value = "流程类型节点编码", required = true, dataType = "String"),
            @ApiImplicitParam(name = "year", value = "年份", required = false, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @PreAuthorize("hasAnyAuthority('admin', 'admin.flow', 'admin.unit')")
    @ResponseBody
    @RequestMapping(value = "/flow/getStatYear", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<com.alibaba.fastjson.JSONObject> getStatYear(@RequestParam(value = "typeCode") String typeCode, Integer year) {
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        /*String priv = "admin.flow";
        if (!privilege.isUserPrivValid(request, priv)) {
            throw new ErrMsgException(i18nUtil.get("pvg_invalid"));
        }*/

        if (StrUtil.isEmpty(typeCode)) {
            if (!privilege.isUserPrivValid(request, "admin")) {
                throw new ErrMsgException(i18nUtil.get("pvg_invalid"));
            }
        }

        JSONObject json = new JSONObject();
        String leafName = "全部";
        if (!"".equals(typeCode)) {
            Leaf lf = new Leaf();
            lf = lf.getLeaf(typeCode);
            leafName = lf.getName();
        }

        Calendar cal = Calendar.getInstance();
        if (year == null) {
            year = cal.get(Calendar.YEAR);
        }

        int totalWorkflow = 0;
        Calendar showCal = Calendar.getInstance();
        MyActionDb mad = new MyActionDb();
        Vector actionV;
        String sql;
        ArrayList al = new ArrayList();
        for (int i = 0; i <= 11; i++) {
            showCal.set(year, i, 1);

            String bdate = SQLFilter.getDateStr(DateUtil.format(showCal, "yyyy-MM-dd 00:00:00"), "yyyy-MM-dd HH:mm:ss");

            Calendar nextCal = Calendar.getInstance();
            if (i <= 10) {
                nextCal.set(year, i + 1, 1);
            } else {
                nextCal.set(year + 1, 0, 1);
            }
            String edate = SQLFilter.getDateStr(DateUtil.format(nextCal, "yyyy-MM-dd 00:00:00"), "yyyy-MM-dd HH:mm:ss");

            // 已办流程
            if ("".equals(typeCode)) {
                sql = "select id from flow where begin_date>=" + bdate + " and begin_date<" + edate + " order by begin_date desc";
            } else {
                sql = "select id from flow where type_code=" + StrUtil.sqlstr(typeCode) + " and begin_date>=" + bdate + " and begin_date<" + edate + " order by begin_date desc";
            }

            actionV = mad.list(sql);
            totalWorkflow += actionV.size();
            al.add(actionV.size());
        }
        json.put("total", totalWorkflow);
        json.put("title", leafName);
        json.put("list", al);
        return new Result<>(json);
    }

    @ApiOperation(value = "流程效率分析", notes = "流程效率分析", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "typeCode", value = "流程类型节点编码", required = true, dataType = "String"),
            @ApiImplicitParam(name = "year", value = "年份", required = false, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @PreAuthorize("hasAnyAuthority('admin', 'admin.flow', 'admin.unit')")
    @ResponseBody
    @RequestMapping(value = "/flow/getAnalysisYear", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<com.alibaba.fastjson.JSONObject> getAnalysisYear(@RequestParam(value = "typeCode") String typeCode, Integer year) {
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        if (StrUtil.isEmpty(typeCode)) {
            if (!privilege.isUserPrivValid(request, "admin")) {
                throw new ErrMsgException(i18nUtil.get("pvg_invalid"));
            }
        }

        JSONObject json = new JSONObject();
        String leafName = "全部";
        if (!"".equals(typeCode)) {
            Leaf lf = new Leaf();
            lf = lf.getLeaf(typeCode);
            leafName = lf.getName();
        }

        Calendar cal = Calendar.getInstance();
        if (year == null) {
            year = cal.get(Calendar.YEAR);
        }

        boolean isCompare = ParamUtil.getBoolean(request, "isCompare", false);

        JSONArray aryYear = new JSONArray();
        for (int i=0; i<12; i++) {
            JSONArray ary = new JSONArray();
            for (Object o : WorkflowActionDb.getActionsPerformance(typeCode, year, i, isCompare)) {
                WorkflowActionDb wad = (WorkflowActionDb) o;
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("name", wad.getJobName());
                jsonObject.put("value", wad.getAveragePerformance() * 100);
                ary.add(jsonObject);
            }
            aryYear.add(ary);
        }
        json.put("title", leafName);
        json.put("list", aryYear);
        return new Result<>(json);
    }

    @ApiOperation(value = "取流程中的节点", notes = "取流程中的节点，用于流程干预", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/getActions", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<com.alibaba.fastjson.JSONArray> getActions(@RequestParam(value = "flowId") Integer flowId) {
        JSONArray ary = new JSONArray();
        WorkflowDb wf = new WorkflowDb();
        wf = wf.getWorkflowDb(flowId);
        for (WorkflowActionDb wa : wf.getActions()) {
            JSONObject json = new JSONObject();
            json.put("id", wa.getId());
            json.put("internalName", wa.getInternalName());
            json.put("title", wa.getTitle());
            json.put("status", wa.getStatus());
            json.put("jobName", wa.getJobName());
            json.put("userRealName", wa.getUserRealName());
            ary.add(json);
        }
        return new Result<>(ary);
    }

    @ApiOperation(value = "删除办理记录", notes = "删除办理记录，用于流程干预", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "delMyActionId", value = "办理记录ID", required = true, dataType = "Long"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @PreAuthorize("hasAnyAuthority('admin', 'admin.flow', 'admin.unit')")
    @ResponseBody
    @RequestMapping(value = "/flow/delMyAction", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<com.alibaba.fastjson.JSONArray> delMyAction(@RequestParam(value = "delMyActionId") Long delMyActionId) {
        boolean re = false;
        MyActionDb mad = new MyActionDb();
        mad = mad.getMyActionDb(delMyActionId);
        WorkflowDb wf = new WorkflowDb();
        wf = wf.getWorkflowDb((int) mad.getFlowId());

        // 删除日程中的待办事宜
        PlanDb pd = new PlanDb();
        pd = pd.getPlanDb(mad.getUserName(), PlanDb.ACTION_TYPE_FLOW, String.valueOf(mad.getId()));
        if (pd != null) {
            pd.del();
        }

        re = mad.del();
        if (re) {
            // 检查是否节点上是否有其它的办理记录，如果没有，则更改节点状态为未处理
            if (mad.listByAction((int) mad.getActionId()).size() == 0) {
                WorkflowActionDb wa = new WorkflowActionDb();
                wa = wa.getWorkflowActionDb((int) mad.getActionId());
                wa.setStatus(WorkflowActionDb.STATE_NOTDO);
                wa.save();
            }

            wf.setIntervenor(authUtil.getUserName());
            wf.setInterveneTime(new Date());
            wf.save();
        }

        return new Result<>(re);
    }

    @ApiOperation(value = "更改流程状态", notes = "更改流程状态，用于流程干预", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "status", value = "状态", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @PreAuthorize("hasAnyAuthority('admin', 'admin.flow', 'admin.unit')")
    @ResponseBody
    @RequestMapping(value = "/flow/changeStatus", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> changeStatus(@RequestParam(value = "flowId") Integer flowId, @RequestParam Integer status) {
        boolean re = false;
        WorkflowDb wf = new WorkflowDb();
        wf = wf.getWorkflowDb(flowId);
        MyActionDb mad = new MyActionDb();
        mad = mad.getLastMyActionDbOfFlow(wf.getId());
        long actionId = mad.getActionId();
        WorkflowActionDb lastAction = new WorkflowActionDb();
        lastAction = lastAction.getWorkflowActionDb((int)actionId);
        int oldStatus = wf.getStatus();
        re = wf.changeStatus(request, status, lastAction);

        // 置所有未结束的mad为已处理状态
        if (status == WorkflowDb.STATUS_FINISHED && oldStatus != status) {
            Vector<MyActionDb> v = mad.getMyActionDbDoingOfFlow(flowId);
            for (MyActionDb myActionDb : v) {
                lastAction.changeMyActionDb(myActionDb, ConstUtil.USER_SYSTEM);
            }
        }

        if (re) {
            // 因为在changeStatus中修改了wf，所以此处需重新获取wf，否则flowstring得不到更新
            wf = wf.getWorkflowDb(flowId);
            wf.setIntervenor(authUtil.getUserName());
            wf.setInterveneTime(new Date());
            wf.save();
        }

        return new Result<>(re);
    }

    @ApiOperation(value = "转交", notes = "转交，用于流程干预", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "internalName", value = "节点内部名称", required = true, dataType = "String"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @PreAuthorize("hasAnyAuthority('admin', 'admin.flow', 'admin.unit')")
    @ResponseBody
    @RequestMapping(value = "/flow/submitTo", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> submitTo(@RequestParam(value = "flowId") Integer flowId, @RequestParam(value = "internalName")String internalName) {
        long myActionId = ParamUtil.getLong(request, "myActionId");
        WorkflowDb wf = new WorkflowDb();
        wf = wf.getWorkflowDb(flowId);
        String users = ParamUtil.get(request, "users");
        String userRealNames = ParamUtil.get(request, "userRealNames");
        WorkflowMgr workflowMgr = new WorkflowMgr();
        WorkflowActionDb wad = new WorkflowActionDb();
        wf = wf.getWorkflowDb(flowId);
        int status = wf.getStatus();
        boolean re = workflowMgr.deliverTo(request, flowId, myActionId, users, userRealNames, internalName);
        if (re) {
            // 如果原来的流程状态为已结束
            if (status == WorkflowDb.STATUS_FINISHED) {
                // 注意要把结束节点原来已完成的变为忽略状态，否则当流程继续流转时，就会因为已有结束节点为已完成状态，导致流程变为已结束状态
                Vector<WorkflowActionDb> v = wad.getEndActionsOfFlow(flowId);
                for (WorkflowActionDb workflowActionDb : v) {
                    wad = workflowActionDb;
                    if (wad.getStatus() == WorkflowActionDb.STATE_FINISHED) {
                        wad.setStatus(WorkflowActionDb.STATE_IGNORED);
                        wad.save();
                    }
                }
            }

            wf.setStatus(WorkflowDb.STATUS_STARTED);
            wf.setIntervenor(authUtil.getUserName());
            wf.setInterveneTime(new Date());
            wf.save();
        }

        return new Result<>(re);
    }

    @ApiOperation(value = "更改节点状态", notes = "更改节点状态，用于流程干预", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "actionId", value = "节点ID", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "status", value = "状态", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @PreAuthorize("hasAnyAuthority('admin', 'admin.flow', 'admin.unit')")
    @ResponseBody
    @RequestMapping(value = "/flow/changeActionStatus", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> changeActionStatus(@RequestParam(value = "actionId") Integer actionId, @RequestParam(value = "status") Integer status) {
        WorkflowActionDb wa = new WorkflowActionDb();
        wa = wa.getWorkflowActionDb(actionId);
        wa.setStatus(status);
        boolean re = wa.save();
        if (re) {
            WorkflowDb wf = new WorkflowDb();
            // 因为在wa.save()中修改了wf，所以此处需重新获取wf，否则flowstring得不到更新
            wf = wf.getWorkflowDb(wa.getFlowId());
            wf.setIntervenor(authUtil.getUserName());
            wf.setInterveneTime(new Date());
            wf.save();
        }
        return new Result<>(re);
    }

    @ApiOperation(value = "批量删除流程", notes = "批量删除流程", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "ids", value = "流程ID，以逗号分隔", required = true, dataType = "String"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/delBatch", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> delBatch(@RequestParam(value="ids") String ids) {
        String[] ary = StrUtil.split(ids, ",");
        if (ary!=null) {
            LeafPriv lp = new LeafPriv();
            for (String s : ary) {
                int flowId = StrUtil.toInt(s);
                WorkflowMgr wm = new WorkflowMgr();
                WorkflowDb wf = wm.getWorkflowDb(flowId);
                // 判断用户是否拥有管理权
                if (!lp.canUserExamine(authUtil.getUserName())) {
                    return new Result<>(false, i18nUtil.get("pvg_invalid"));
                }
                wf.del();
            }
        }
        return new Result<>(true);
    }

    @ApiOperation(value = "删至回收站", notes = "删至回收站", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "ids", value = "流程ID，以逗号分隔", required = true, dataType = "String"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/delToDustbin", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> delToDustbin(@RequestParam(value="ids") String ids) {
        String[] ary = StrUtil.split(ids, ",");
        if (ary!=null) {
            LeafPriv lp = new LeafPriv();
            for (String s : ary) {
                int flowId = StrUtil.toInt(s);
                WorkflowMgr wm = new WorkflowMgr();
                // 判断用户是否拥有管理权
                if (!lp.canUserExamine(authUtil.getUserName())) {
                    return new Result<>(false, i18nUtil.get("pvg_invalid"));
                }
                wm.del(request, flowId);
            }
        }
        return new Result<>(true);
    }

    @ApiOperation(value = "批量放弃", notes = "批量放弃", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "ids", value = "流程ID，以逗号分隔", required = true, dataType = "String"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/discardBatch", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> discardBatch(@RequestParam(value="ids") String ids) {
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        String[] ary = StrUtil.split(ids, ",");
        String userName = privilege.getUser(request);
        for (int i = 0; i < ary.length; i++) {
            int flowId = StrUtil.toInt(ary[i], -1);
            WorkflowDb wf = new WorkflowDb();
            wf = wf.getWorkflowDb(flowId);
            try {
                wf.discard(userName);
            } catch (ErrMsgException e) {
                return new Result<>(false, e.getMessage());
            }
        }
        return new Result<>(true);
    }

    @ApiOperation(value = "更新流程", notes = "更新流程，使节点、连线属性修改后能生效", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowId", value = "流程ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/refreshFlow", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> refreshFlow(@RequestParam(value="flowId") Integer flowId) {
        WorkflowDb wf = new WorkflowDb();
        wf = wf.getWorkflowDb(flowId);
        try {
            wf.refreshFlow();
        } catch (ErrMsgException e) {
            return new Result<>(false, e.getMessage());
        }
        return new Result<>(true);
    }

    @ApiOperation(value = "批量更新流程", notes = "批量更新流程，使节点、连线属性修改后能生效", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "ids", value = "流程ID，以逗号分隔", required = true, dataType = "String"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/refreshFlowBatch", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> refreshFlowBatch(@RequestParam(value="ids") String ids) {
        String[] ary = StrUtil.split(ids, ",");
        for (String s : ary) {
            int flowId = StrUtil.toInt(s, -1);

            WorkflowDb wf = new WorkflowDb();
            wf = wf.getWorkflowDb(flowId);
            try {
                wf.refreshFlow();
            } catch (ErrMsgException e) {
                return new Result<>(false, e.getMessage());
            }
        }
        return new Result<>(true);
    }

    @ApiOperation(value = "流程类型列表", notes = "流程类型列表，用于流程移交", httpMethod = "POST")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @PreAuthorize("hasAnyAuthority('admin', 'admin.flow', 'admin.unit')")
    @ResponseBody
    @RequestMapping(value = "/flow/listFlowTypeForHandover", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> listFlowTypeForHandover() {
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        JSONArray ary = new JSONArray();
        Leaf lf = new Leaf();
        lf = lf.getLeaf(Leaf.CODE_ROOT);
        for (Leaf leaf : lf.getChildren()) {
            lf = leaf;

            LeafPriv lp = new LeafPriv(lf.getCode());
            if (!lp.canUserExamine(authUtil.getUserName())) {
                continue;
            }

            if (lf.getType() != Leaf.TYPE_NONE) {
                JSONObject json = new JSONObject();
                json.put("name", lf.getName());
                json.put("code", lf.getCode());
                ary.add(json);
            } else {
                for (Object o : lf.getAllChildOfUnit(new Vector(), lf, privilege.getUserUnitCode(request))) {
                    lf = (Leaf) o;
                    if (lf.getType() != Leaf.TYPE_NONE) {
                        JSONObject json = new JSONObject();
                        json.put("name", lf.getName(request));
                        json.put("code", lf.getCode());
                        ary.add(json);
                    }
                }
            }
        }
        return new Result<>(ary);
    }

    @ApiOperation(value = "流程移交", notes = "流程移交", httpMethod = "POST")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @PreAuthorize("hasAnyAuthority('admin', 'admin.flow', 'admin.unit')")
    @ResponseBody
    @RequestMapping(value = "/flow/handover", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> handover() {
        JSONObject json = new JSONObject();
        WorkflowMgr wfm = new WorkflowMgr();
        String[] typeCode = ParamUtil.getParameters(request, "typeCode");
        int ret = 0;
        try {
            ret = wfm.handover(request);
        }
        catch (ErrMsgException e) {
            return new Result<>(false, e.getMessage());
        }
        json.put("count", ret);
        return new Result<>(json);
    }

    @ApiOperation(value = "运行脚本", notes = "运行脚本，用于调度中心", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "script", value = "脚本", required = true, dataType = "String"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/runScript", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> runScript(@RequestParam(value = "script") String myscript) {
        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        BSHShell bsh = new BSHShell();
        StringBuilder sbMsg = new StringBuilder();
        try {
            bsh.set("sbMsg", sbMsg);

            // 赋值给用户
            bsh.set("request", request);
            bsh.eval("userName=\"" + privilege.getUser(request) + "\";");

            myscript = myscript.replaceAll("System.out.println\\(", "sbMsg.append\\(\"<BR>\" + ");
            myscript = myscript.replaceAll("System.out.print\\(", "sbMsg.append\\(");
            bsh.eval(myscript);

            sbMsg = (StringBuilder) bsh.get("sbMsg");
        } catch (Exception e) {
            sbMsg.append(StrUtil.toHtml(StrUtil.trace(e)));
            sbMsg.append("<BR />-----------------操作失败，以下为脚本----------------------<BR />");
            sbMsg.append(StrUtil.toHtml(myscript));
        }

        sbMsg.append("<BR />-----------------操作成功，以下为脚本----------------------<BR />");
        sbMsg.append(StrUtil.toHtml(myscript));

        JSONObject json = new JSONObject();
        json.put("result", sbMsg.toString());
        return new Result<>(json);
    }

    @ApiOperation(value = "获取流程类型的统计", notes = "流程移交", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "typeCode", value = "流程类型编码", required = true, dataType = "String"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @PreAuthorize("hasAnyAuthority('admin', 'admin.flow', 'admin.unit')")
    @ResponseBody
    @RequestMapping(value = "/flow/getMiniStateByTypeCode", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> getMiniStateByTypeCode(@RequestParam String typeCode) {
        return new Result<>(workflowService.getMiniStateByTypeCode(typeCode));
    }

    @ApiOperation(value = "打包下载文件", notes = "打包下载文件", httpMethod = "GET")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowIds", value = "所选的记录ID", required = false, dataType = "String"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @RequestMapping("/flow/downloadZipFile")
    public void downloadZipFile(HttpServletResponse response, @RequestParam(value="flowIds")String flowIds) throws IOException, ErrMsgException {
        WorkflowDb wf = new WorkflowDb();
        String[] ary = StrUtil.split(flowIds, ",");
        if (ary == null) {
            return;
        }

        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        String userName = authUtil.getUserName();
        FormDb fd = new FormDb();
        FormViewDb formViewDb = new FormViewDb();
        ModuleSetupDb msd = new ModuleSetupDb();
        com.redmoon.oa.flow.FormDAO fdao = new com.redmoon.oa.flow.FormDAO();
        com.redmoon.oa.visual.Render rd = new com.redmoon.oa.visual.Render(request, fd);

        IFileService fileService = SpringUtil.getBean(IFileService.class);
        String basePath = FileUpload.getTempPath() + File.separator + RandomSecquenceCreator.getId(20);
        File fBase = new File(basePath);
        if (!fBase.isDirectory()) {
            fBase.mkdirs();
        }

        // 用于ImageCtl中生成完整路径图片
        request.setAttribute("pageType", ConstUtil.PAGE_TYPE_WORD);

        for (String strFlowId : ary) {
            int flowId = StrUtil.toInt(strFlowId, -1);
            wf = wf.getWorkflowDb(flowId);

            boolean isFlowManager = false;
            LeafPriv lp = new LeafPriv(wf.getTypeCode());
            if (privilege.isUserPrivValid(request, "admin.flow")) {
                if (lp.canUserExamine(userName)) {
                    isFlowManager = true;
                }
            }
            boolean canUserView = lp.canUserQuery(userName);
            if (!canUserView) {
                canUserView = privilege.isUserPrivValid(request, "paper.receive");
            }
            // 判断是否拥有查看流程过程的权限
            if (!isFlowManager && !canUserView) {
                // 判断是否参与了流程
                if (!wf.isUserAttended(userName)) {
                    throw new ErrMsgException(wf.getTitle() + ": " + i18nUtil.get("pvg_invalid"));
                }
            }

            Leaf lf = new Leaf();
            lf = lf.getLeaf(wf.getTypeCode());
            String formCode = lf.getFormCode();
            fd = fd.getFormDb(formCode);

            String formContent = fd.getContent();
            int formViewId = ParamUtil.getInt(request, "formViewId", -1);
            if (formViewId != -1) {
                formViewDb = formViewDb.getFormViewDb(formViewId);
                // 默认取第一个视图以便于导出
                if (formViewDb.isLoaded()) {
                    formContent = formViewDb.getString("form");
                } else {
                    throw new ErrMsgException("视图不存在, formViewId:" + formViewId);
                }
            }

            msd = msd.getModuleSetupDb(lf.getFormCode());

            fdao = fdao.getFormDAOByCache(flowId, fd);

            String zipName = String.valueOf(flowId);
            int zipOnlyAttachment = 0;
            String strProps = msd.getString("props");
            if (!StrUtil.isEmpty(strProps)) {
                JSONObject props = JSONObject.parseObject(msd.getString("props"));
                if (props.containsKey("zipName") && !StrUtil.isEmpty(props.getString("zipName"))) {
                    zipName = props.getString("zipName");
                    zipName = ModuleUtil.parseField(fdao, zipName);
                }
                if (props.containsKey("zipOnlyAttachment")) {
                    zipOnlyAttachment = props.getIntValue("zipOnlyAttachment");
                }
            }

            String dir = basePath + File.separator + zipName;
            File f = new File(dir);
            if (!f.isDirectory()) {
                f.mkdirs();
            }

            // 生成word文件
            if (zipOnlyAttachment == 0) {
                if (formViewDb.isLoaded()) {
                    formContent = com.redmoon.oa.visual.Render.applyShowRule(request, formViewDb, fd, fdao, authUtil.getUserName());
                }
                String content = moduleService.filterString(rd.reportForArchive(fdao, formContent));
                BufferedOutputStream bos = null;
                try {
                    String randName = zipName + ".doc";
                    File tmpFile = new File(dir + File.separator + randName);
                    tmpFile.createNewFile();
                    bos = new BufferedOutputStream(new FileOutputStream(tmpFile));
                    WordUtil.htmlToWord(content, bos);
                } catch (final Exception e) {
                    LogUtil.getLog(getClass()).error(e);
                } finally {
                    if (bos != null) {
                        bos.close();
                    }
                }
            }

            Vector<IAttachment> v = fdao.getAttachments();
            for (IAttachment attachment : v) {
                fileService.copyToLocalFile(attachment.getName(), attachment.getVisualPath(), attachment.getDiskName(), dir);
            }
        }

        // 压缩至zip文件
        FileOutputStream fos = new FileOutputStream(new File(basePath + ".zip"));
        CheckedOutputStream ch = new CheckedOutputStream(fos, new CRC32());
        ZipOutputStream out = new ZipOutputStream(ch);
        zipUtil.zipDir(out, basePath);
        out.close();
        fos.close();

        String zipTmpFileName = basePath + ".zip";
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(zipTmpFileName)); BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream())) {
            byte[] buff = new byte[2048];
            int bytesRead;

            while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
                bos.write(buff, 0, bytesRead);
            }
        } catch (final IOException e) {
            LogUtil.getLog(getClass()).error(e);
        } finally {
            // 删除生成的临时文件
            cn.js.fan.util.file.FileUtil.del(basePath);
            FileUtil.del(zipTmpFileName);
        }
    }

    @ApiOperation(value = "已阅", notes = "已阅", httpMethod = "POST")
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/readed", method={RequestMethod.POST}, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> readed() {
        WorkflowMgr wfm = new WorkflowMgr();
        boolean re;
        try {
            try {
                wfm.doUpload(request.getServletContext(), request);
            } catch (ErrMsgException e) {
                // 可能会抛出验证非法的错误信息
                LogUtil.getLog(getClass()).error(e);
                return new Result<>(false, e.getMessage());
            }

            request.setAttribute("workflowParams", new WorkflowParams(request, wfm.getFileUpload()));

            long myActionId = StrUtil.toLong(wfm.getFileUpload().getFieldValue("myActionId"), -1);
            MyActionDb mad = new MyActionDb();
            mad = mad.getMyActionDb(myActionId);
            WorkflowDb wf = new WorkflowDb();
            wf = wf.getWorkflowDb((int)mad.getFlowId());
            WorkflowActionDb wa = new WorkflowActionDb();
            wa = wa.getWorkflowActionDb((int)mad.getActionId());
            // 保存表单内容
            re = wfm.saveFormValue(request, wf, wa);
            if (re) {
                re = wfm.readed(request, myActionId);
            }
        } catch (ErrMsgException e) {
            return new Result<>(responseUtil.getResJson(false, e.getMessage()));
        }

        return new Result<>(re);
    }

    @ApiOperation(value = "取附件信息", notes = "取附件信息", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "附件ID", required = true, dataType = "Long"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/getAttachment", method={RequestMethod.POST}, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> getAttachment(@RequestParam Long id) {
        Attachment att = new Attachment(id);
        return new Result<>(att);
    }

    @ApiOperation(value = "强制办结", notes = "强制办结", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "流程ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/forceFinish", method={RequestMethod.POST}, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> forceFinish(@RequestParam Integer flowId) {
        WorkflowDb wf = new WorkflowDb();
        wf = wf.getWorkflowDb(flowId);

        com.redmoon.oa.pvg.Privilege privilege = new com.redmoon.oa.pvg.Privilege();
        if (!privilege.isUserPrivValid(request, "admin")) {
            // 判断是否有管理权限
            LeafPriv leafPriv = new LeafPriv(wf.getTypeCode());
            if (!leafPriv.canUserExamine(authUtil.getUserName())) {
                return new Result<>(false, i18nUtil.get("pvg_invalid"));
            }
        }

        MyActionDb mad = new MyActionDb();
        mad = mad.getLastMyActionDbDoneOfFlow(flowId);

        WorkflowActionDb lastAction = new WorkflowActionDb();
        lastAction = lastAction.getWorkflowActionDb((int)mad.getActionId());

        return new Result<>(wf.changeStatus(request, WorkflowDb.STATUS_FINISHED, lastAction));
    }

    @ApiOperation(value = "取得条件分支节点上条件所关联的字段", notes = "取得条件分支节点上条件所关联的字段", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "actionId", value = "节点ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/getFieldNamesInCond", method={RequestMethod.POST}, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> getFieldNamesInCond(@RequestParam Integer actionId) {
        List<String> list = WorkflowRouter.getFieldNamesInCond(actionId);
        return new Result<>(list);
    }

    @ApiOperation(value = "取节点后续的分支上的节点，以便于发起人选择人员", notes = "取节点后续的分支上的节点，以便于发起人选择人员", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "actionId", value = "节点ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/getActionsOnBranch", method={RequestMethod.POST}, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> getActionsOnBranch(@RequestParam Integer actionId) {
        WorkflowActionDb wad = new WorkflowActionDb();
        wad = wad.getWorkflowActionDb(actionId);

        WorkflowDb wf = new WorkflowDb();
        wf = wf.getWorkflowDb(wad.getFlowId());

        List<WorkflowActionDb> list = new ArrayList<>();
        WorkflowMgr wm = new WorkflowMgr();
        wm.getActionsOnBranch(wad, list);
        JSONArray ary = new JSONArray();
        for (WorkflowActionDb workflowActionDb : list) {
            if(StrUtil.toInt(workflowHelper.tran(workflowService.getActionPropByFlowJson(wf, workflowActionDb, "ActionApproverType")), 0)==0) {
                continue;
            }

            JSONObject json = new JSONObject();
            json.put("id", workflowActionDb.getId());
            json.put("title", workflowActionDb.getTitle());

            json.put("ActionApplicantSelMode", StrUtil.toInt(workflowHelper.tran(workflowService.getActionPropByFlowJson(wf, workflowActionDb, "ActionApplicantSelMode")), 0));
            json.put("ActionApplicantSelScope", StrUtil.toInt(workflowHelper.tran(workflowService.getActionPropByFlowJson(wf, workflowActionDb, "ActionApplicantSelScope")), 0));
            JSONArray aryUsers = JSONArray.parseArray(workflowHelper.tran(workflowService.getActionPropByFlowJson(wf, workflowActionDb, "ActionApplicantAssignedUsers")));
            json.put("ActionApplicantAssignedUsers", aryUsers == null ? new JSONArray() : aryUsers);
            JSONArray aryRoles = JSONArray.parseArray(workflowHelper.tran(workflowService.getActionPropByFlowJson(wf, workflowActionDb, "ActionApplicantAssignedRoles")));
            json.put("ActionApplicantAssignedRoles", aryRoles == null ? new JSONArray() : aryRoles);

            JSONArray aryUserSel = new JSONArray();
            if (workflowActionDb.getNodeMode() == WorkflowActionDb.NODE_MODE_USER || workflowActionDb.getNodeMode() == WorkflowActionDb.NODE_MODE_USER_SELECTED) {
                String[] aryName = StrUtil.split(workflowActionDb.getJobCode(), ",");
                if (aryName != null) {
                    String[] aryRealName = StrUtil.split(workflowActionDb.getJobName(), ",");
                    for (int i = 0; i < aryName.length; i++) {
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put("userName", aryName[i]);
                        jsonObject.put("realName", aryRealName[i]);
                        aryUserSel.add(jsonObject);
                    }
                }
            }
            json.put("usersSelected", aryUserSel);

            ary.add(json);
        }
        return new Result<>(ary);
    }

    @ApiOperation(value = "置节点上的处理人员", notes = "置节点上的处理人员", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "actionId", value = "节点ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({ @ApiResponse(code = 200, message = "操作成功") })
    @ResponseBody
    @RequestMapping(value = "/flow/setActionUser", method={RequestMethod.POST}, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> setActionUser(@RequestBody JSONObject json) {
        int id = json.getIntValue("id");
        JSONArray users = json.getJSONArray("users");

        WorkflowActionDb wad = new WorkflowActionDb();
        wad = wad.getWorkflowActionDb(id);

        StringBuilder sbUser = new StringBuilder();
        StringBuilder sbRealName = new StringBuilder();
        for (Object o : users) {
            JSONObject jsonObject = (JSONObject)o;
            StrUtil.concat(sbUser, ",", jsonObject.getString("userName"));
            StrUtil.concat(sbRealName, ",", jsonObject.getString("realName"));
        }

        wad.setJobCode(sbUser.toString());
        wad.setJobName(sbRealName.toString());
        wad.setNodeMode(WorkflowActionDb.NODE_MODE_USER);
        boolean re = wad.save();
        return new Result<>(re);
    }
}
